@gesslar/toolkit 2.8.0 → 2.10.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gesslar/toolkit",
3
- "version": "2.8.0",
3
+ "version": "2.10.0",
4
4
  "description": "A collection of utilities for Node.js and browser environments.",
5
5
  "main": "./src/index.js",
6
6
  "type": "module",
@@ -13,6 +13,7 @@ import path from "node:path"
13
13
  import {Data, Valid} from "../browser/index.js"
14
14
  import DirectoryObject from "./DirectoryObject.js"
15
15
  import FileObject from "./FileObject.js"
16
+ import FS from "./FS.js"
16
17
  import Sass from "./Sass.js"
17
18
 
18
19
  /**
@@ -30,88 +31,80 @@ export default class CappedDirectoryObject extends DirectoryObject {
30
31
  /**
31
32
  * Constructs a CappedDirectoryObject instance.
32
33
  *
33
- * This is an abstract base class - use subclasses like TempDirectoryObject
34
- * that define specific caps.
34
+ * Without a parent, the path becomes both the directory location and the cap
35
+ * (virtual root). With a parent, the path is resolved relative to the parent's
36
+ * cap using virtual path semantics (absolute paths treated as cap-relative).
35
37
  *
36
- * @param {string?} name - Base name for the directory (if empty/null, uses cap root)
37
- * @param {string} cap - The root path that constrains this directory tree
38
+ * @param {string} dirPath - Directory path (becomes cap if no parent, else relative to parent's cap)
38
39
  * @param {CappedDirectoryObject?} [parent] - Optional parent capped directory
39
40
  * @param {boolean} [temporary=false] - Whether this is a temporary directory
40
- * @throws {Sass} If name is absolute
41
- * @throws {Sass} If name is empty (when parent is provided)
42
- * @throws {Sass} If name contains path separators
43
- * @throws {Sass} If parent is not a capped directory
44
- * @throws {Sass} If parent's lineage does not trace back to the cap
41
+ * @throws {Sass} If path is empty
42
+ * @throws {Sass} If parent is provided but not a CappedDirectoryObject
45
43
  * @throws {Sass} If the resulting path would escape the cap
44
+ * @example
45
+ * // Create new capped directory
46
+ * const cache = new CappedDirectoryObject("/home/user/.cache")
47
+ * // path: /home/user/.cache, cap: /home/user/.cache
48
+ *
49
+ * @example
50
+ * // Create subdirectory with parent
51
+ * const data = new CappedDirectoryObject("data", cache)
52
+ * // path: /home/user/.cache/data, cap: /home/user/.cache
53
+ *
54
+ * @example
55
+ * // Virtual absolute path with parent
56
+ * const config = new CappedDirectoryObject("/etc/config", cache)
57
+ * // path: /home/user/.cache/etc/config, cap: /home/user/.cache
46
58
  */
47
- constructor(name, cap, parent=null, temporary=false) {
48
- Valid.type(cap, "String")
59
+ constructor(dirPath, parent=null, temporary=false) {
60
+ Valid.type(dirPath, "String")
61
+ Valid.assert(dirPath.length > 0, "Path must not be empty.")
49
62
 
50
63
  // Validate parent using instanceof since TypeSpec doesn't understand inheritance
51
64
  if(parent !== null && !(parent instanceof CappedDirectoryObject)) {
52
65
  throw Sass.new(`Parent must be null or a CappedDirectoryObject instance, got ${Data.typeOf(parent)}`)
53
66
  }
54
67
 
55
- let dirPath
68
+ let cap
69
+ let resolvedPath
56
70
 
57
- // Special case: empty name with no parent means use cap root
58
- if(!name && !parent) {
59
- dirPath = cap
71
+ if(!parent) {
72
+ // No parent: dirPath becomes both the directory and the cap
73
+ cap = path.resolve(dirPath)
74
+ resolvedPath = cap
60
75
  } else {
61
- Valid.type(name, "String")
76
+ // With parent: inherit cap and resolve dirPath relative to it
77
+ cap = parent.#cap
62
78
 
63
- // Security: Validate name before any processing
64
- Valid.assert(
65
- !path.isAbsolute(name),
66
- "Capped directory name must not be an absolute path.",
67
- )
68
- Valid.assert(
69
- name.length > 0,
70
- "Capped directory name must not be empty.",
71
- )
72
- Valid.assert(
73
- !name.includes("/") && !name.includes("\\") && !name.includes(path.sep),
74
- "Capped directory name must not contain path separators.",
75
- )
79
+ // Use real path for filesystem operations
80
+ const parentPath = parent.realPath || parent.path
81
+ const capResolved = path.resolve(cap)
76
82
 
77
- if(parent) {
78
- // Ensure parent is capped
79
- Valid.assert(parent.capped, "Parent must be a capped DirectoryObject.")
80
-
81
- // Ensure parent has same cap
82
- Valid.assert(
83
- parent.cap === cap,
84
- "Parent must have the same cap as this directory.",
85
- )
86
-
87
- const parentPath = parent.path
88
-
89
- // Validate parent's lineage traces back to the cap
90
- let found = false
91
- if(parent.trail) {
92
- for(const p of parent.walkUp) {
93
- if(p.path === cap) {
94
- found = true
95
- break
96
- }
97
- }
98
- }
99
-
100
- Valid.assert(
101
- found,
102
- `The lineage of this directory must trace back to the cap '${cap}'.`,
103
- )
104
-
105
- dirPath = path.join(parentPath, name)
83
+ let targetPath
84
+
85
+ // If absolute, treat as virtual path relative to cap (strip leading /)
86
+ if(path.isAbsolute(dirPath)) {
87
+ const relative = dirPath.replace(/^[/\\]+/, "")
88
+ targetPath = relative ? path.join(capResolved, relative) : capResolved
89
+ } else {
90
+ // Relative path - resolve from parent directory
91
+ targetPath = FS.resolvePath(parentPath, dirPath)
92
+ }
93
+
94
+ // Resolve to absolute path (handles .. and .)
95
+ const resolved = path.resolve(targetPath)
96
+
97
+ // Clamp to cap boundary - cannot escape above cap
98
+ if(!resolved.startsWith(capResolved)) {
99
+ // Path tried to escape - clamp to cap root
100
+ resolvedPath = capResolved
106
101
  } else {
107
- // No parent - this is a root-level capped directory
108
- dirPath = path.join(cap, name)
102
+ resolvedPath = resolved
109
103
  }
110
104
  }
111
105
 
112
106
  // Call parent constructor with the path
113
- // Pass through the temporary flag (subclasses control this)
114
- super(dirPath, temporary)
107
+ super(resolvedPath, temporary)
115
108
 
116
109
  // Store the cap AFTER calling super()
117
110
  this.#cap = cap
@@ -128,13 +121,13 @@ export default class CappedDirectoryObject extends DirectoryObject {
128
121
  */
129
122
  #validateCapPath() {
130
123
  const cap = this.#cap
131
- const resolved = path.resolve(this.path)
124
+ const resolved = path.resolve(this.#realPath)
132
125
  const capResolved = path.resolve(cap)
133
126
 
134
127
  // Check if the resolved path starts with the cap directory
135
128
  if(!resolved.startsWith(capResolved)) {
136
129
  throw Sass.new(
137
- `Path '${this.path}' is not within the cap directory '${cap}'`
130
+ `Path '${this.#realPath}' is not within the cap directory '${cap}'`
138
131
  )
139
132
  }
140
133
  }
@@ -157,6 +150,73 @@ export default class CappedDirectoryObject extends DirectoryObject {
157
150
  return true
158
151
  }
159
152
 
153
+ /**
154
+ * Returns the real filesystem path (for internal and subclass use).
155
+ *
156
+ * @protected
157
+ * @returns {string} The actual filesystem path
158
+ */
159
+ get realPath() {
160
+ return super.path
161
+ }
162
+
163
+ /**
164
+ * Private alias for realPath (for use in private methods).
165
+ *
166
+ * @private
167
+ * @returns {string} The actual filesystem path
168
+ */
169
+ get #realPath() {
170
+ return this.realPath
171
+ }
172
+
173
+ /**
174
+ * Returns the virtual path relative to the cap.
175
+ * This is the default path representation in the capped environment.
176
+ * Use `.real.path` to access the actual filesystem path.
177
+ *
178
+ * @returns {string} Path relative to cap, or "/" if at cap root
179
+ * @example
180
+ * const temp = new TempDirectoryObject("myapp")
181
+ * const subdir = temp.getDirectory("data/cache")
182
+ * console.log(subdir.path) // "/data/cache" (virtual, relative to cap)
183
+ * console.log(subdir.real.path) // "/tmp/myapp-ABC123/data/cache" (actual filesystem)
184
+ */
185
+ get path() {
186
+ const capResolved = path.resolve(this.#cap)
187
+ const relative = path.relative(capResolved, this.#realPath)
188
+
189
+ // If at cap root or empty, return "/"
190
+ if(!relative || relative === ".") {
191
+ return "/"
192
+ }
193
+
194
+ // Return with leading slash to indicate it's cap-relative
195
+ return "/" + relative.split(path.sep).join("/")
196
+ }
197
+
198
+ /**
199
+ * Returns a plain DirectoryObject representing the actual filesystem location.
200
+ * This provides an "escape hatch" from the capped environment to interact
201
+ * with the real filesystem when needed.
202
+ *
203
+ * @returns {DirectoryObject} Uncapped directory object at the real filesystem path
204
+ * @example
205
+ * const temp = new TempDirectoryObject("myapp")
206
+ * const subdir = temp.getDirectory("data")
207
+ *
208
+ * // Work within the capped environment (virtual paths)
209
+ * console.log(subdir.path) // "/data" (virtual)
210
+ * subdir.getFile("config.json") // Stays within cap
211
+ *
212
+ * // Break out to real filesystem when needed
213
+ * console.log(subdir.real.path) // "/tmp/myapp-ABC123/data" (real)
214
+ * subdir.real.parent // Can traverse outside the cap
215
+ */
216
+ get real() {
217
+ return new DirectoryObject(this.#realPath)
218
+ }
219
+
160
220
  /**
161
221
  * Returns the parent directory of this capped directory.
162
222
  * Returns null only if this directory is at the cap (the "root" of the capped tree).
@@ -175,12 +235,17 @@ export default class CappedDirectoryObject extends DirectoryObject {
175
235
  const capResolved = path.resolve(this.#cap)
176
236
 
177
237
  // If we're at the cap, return null (cap is the "root")
178
- if(this.path === capResolved) {
238
+ if(this.#realPath === capResolved) {
179
239
  return null
180
240
  }
181
241
 
182
- // Otherwise return the parent (plain DirectoryObject, not capped)
183
- return super.parent
242
+ // Otherwise return the parent using real path (plain DirectoryObject, not capped)
243
+ const parentPath = path.dirname(this.#realPath)
244
+ const isRoot = parentPath === this.#realPath
245
+
246
+ return isRoot
247
+ ? null
248
+ : new DirectoryObject(parentPath, this.temporary)
184
249
  }
185
250
 
186
251
  /**
@@ -201,19 +266,27 @@ export default class CappedDirectoryObject extends DirectoryObject {
201
266
  *#walkUpCapped() {
202
267
  const capResolved = path.resolve(this.#cap)
203
268
 
204
- // Use super.walkUp but stop when we would go beyond the cap
205
- for(const dir of super.walkUp) {
269
+ // Build trail from real path
270
+ const trail = this.#realPath.split(path.sep).filter(Boolean)
271
+ const curr = [...trail]
272
+
273
+ while(curr.length > 0) {
274
+ const joined = path.sep + curr.join(path.sep)
275
+
206
276
  // Don't yield anything beyond the cap
207
- if(!dir.path.startsWith(capResolved)) {
277
+ if(!joined.startsWith(capResolved)) {
208
278
  break
209
279
  }
210
280
 
211
- yield dir
281
+ // Yield plain DirectoryObject with real path
282
+ yield new DirectoryObject(joined, this.temporary)
212
283
 
213
284
  // Stop after yielding the cap
214
- if(dir.path === capResolved) {
285
+ if(joined === capResolved) {
215
286
  break
216
287
  }
288
+
289
+ curr.pop()
217
290
  }
218
291
  }
219
292
 
@@ -229,69 +302,269 @@ export default class CappedDirectoryObject extends DirectoryObject {
229
302
  /**
230
303
  * Creates a new CappedDirectoryObject by extending this directory's path.
231
304
  *
232
- * Validates that the resulting path remains within the cap directory tree.
305
+ * All paths are coerced to remain within the cap directory tree:
306
+ * - Absolute paths (e.g., "/foo") are treated as relative to the cap
307
+ * - Parent traversal ("..") is allowed but clamped at the cap boundary
308
+ * - The cap acts as the virtual root directory
233
309
  *
234
- * @param {string} newPath - The path segment to append
235
- * @returns {CappedDirectoryObject} A new CappedDirectoryObject with the extended path
236
- * @throws {Sass} If the path would escape the cap directory
237
- * @throws {Sass} If the path is absolute
238
- * @throws {Sass} If the path contains traversal (..)
310
+ * @param {string} newPath - The path to resolve (can be absolute or contain ..)
311
+ * @returns {CappedDirectoryObject} A new CappedDirectoryObject with the coerced path
239
312
  * @example
240
313
  * const capped = new TempDirectoryObject("myapp")
241
314
  * const subDir = capped.getDirectory("data")
242
315
  * console.log(subDir.path) // "/tmp/myapp-ABC123/data"
316
+ *
317
+ * @example
318
+ * // Absolute paths are relative to cap
319
+ * const abs = capped.getDirectory("/foo/bar")
320
+ * console.log(abs.path) // "/tmp/myapp-ABC123/foo/bar"
321
+ *
322
+ * @example
323
+ * // Excessive .. traversal clamps to cap
324
+ * const up = capped.getDirectory("../../../etc/passwd")
325
+ * console.log(up.path) // "/tmp/myapp-ABC123" (clamped to cap)
243
326
  */
244
327
  getDirectory(newPath) {
245
328
  Valid.type(newPath, "String")
246
329
 
247
- // Prevent absolute paths
330
+ // Fast path: if it's a simple name (no separators, not absolute, no ..)
331
+ // use the subclass constructor directly to preserve type
332
+ const isSimpleName = !path.isAbsolute(newPath) &&
333
+ !newPath.includes("/") &&
334
+ !newPath.includes("\\") &&
335
+ !newPath.includes("..")
336
+
337
+ if(isSimpleName) {
338
+ // Both CappedDirectoryObject and subclasses use same signature now
339
+ return new this.constructor(newPath, this, this.temporary)
340
+ }
341
+
342
+ // Complex path - handle coercion
343
+ const capResolved = path.resolve(this.#cap)
344
+ let targetPath
345
+
346
+ // If absolute, treat as relative to cap (virtual root)
248
347
  if(path.isAbsolute(newPath)) {
249
- throw Sass.new("Absolute paths are not allowed in capped directories")
348
+ // Strip leading slashes to make relative
349
+ const relative = newPath.replace(/^[/\\]+/, "")
350
+
351
+ // Join with cap (unless empty, which means cap root)
352
+ targetPath = relative ? path.join(capResolved, relative) : capResolved
353
+ } else {
354
+ // Relative path - resolve from current directory
355
+ targetPath = FS.resolvePath(this.#realPath, newPath)
250
356
  }
251
357
 
252
- // Prevent path traversal attacks
253
- const normalized = path.normalize(newPath)
254
- if(normalized.includes("..")) {
255
- throw Sass.new("Path traversal (..) is not allowed in capped directories")
358
+ // Resolve to absolute path (handles .. and .)
359
+ const resolved = path.resolve(targetPath)
360
+
361
+ // Coerce: if path escaped cap, clamp to cap boundary
362
+ const coerced = resolved.startsWith(capResolved)
363
+ ? resolved
364
+ : capResolved
365
+
366
+ // Compute path relative to cap for reconstruction
367
+ const relativeToCap = path.relative(capResolved, coerced)
368
+
369
+ // If we're at the cap root, return cap root directory
370
+ if(!relativeToCap || relativeToCap === ".") {
371
+ return this.#createCappedAtRoot()
256
372
  }
257
373
 
258
- // Use the constructor of the current class (supports subclassing)
259
- // Pass this as parent so the child inherits the same cap
260
- return new this.constructor(newPath, this)
374
+ // Build directory by traversing segments from cap
375
+ return this.#buildDirectoryFromRelativePath(relativeToCap)
376
+ }
377
+
378
+ /**
379
+ * Creates a CappedDirectoryObject at the cap root.
380
+ * Can be overridden by subclasses that have different root semantics.
381
+ *
382
+ * @private
383
+ * @returns {CappedDirectoryObject} Directory object at cap root
384
+ */
385
+ #createCappedAtRoot() {
386
+ // Create a base CappedDirectoryObject at the cap path
387
+ // This works for direct usage of CappedDirectoryObject
388
+ // Subclasses may need to override if they have special semantics
389
+ return new CappedDirectoryObject(this.#cap, null, this.temporary)
390
+ }
391
+
392
+ /**
393
+ * Builds a directory by traversing path segments from cap.
394
+ *
395
+ * @private
396
+ * @param {string} relativePath - Path relative to cap
397
+ * @returns {CappedDirectoryObject} The directory at the final path
398
+ */
399
+ #buildDirectoryFromRelativePath(relativePath) {
400
+ const segments = relativePath.split(path.sep).filter(Boolean)
401
+
402
+ // Start at cap root
403
+ let current = this.#createCappedAtRoot()
404
+
405
+ // Traverse each segment, creating CappedDirectoryObject instances
406
+ // (not subclass instances, to avoid constructor signature issues)
407
+ for(const segment of segments) {
408
+ current = new CappedDirectoryObject(segment, current, this.temporary)
409
+ }
410
+
411
+ return current
261
412
  }
262
413
 
263
414
  /**
264
415
  * Creates a new FileObject by extending this directory's path.
265
416
  *
266
- * Validates that the resulting path remains within the cap directory tree.
417
+ * All paths are coerced to remain within the cap directory tree:
418
+ * - Absolute paths (e.g., "/config.json") are treated as relative to the cap
419
+ * - Parent traversal ("..") is allowed but clamped at the cap boundary
420
+ * - The cap acts as the virtual root directory
267
421
  *
268
- * @param {string} filename - The filename to append
269
- * @returns {FileObject} A new FileObject with the extended path
270
- * @throws {Sass} If the path would escape the cap directory
271
- * @throws {Sass} If the path is absolute
272
- * @throws {Sass} If the path contains traversal (..)
422
+ * @param {string} filename - The filename to resolve (can be absolute or contain ..)
423
+ * @returns {FileObject} A new FileObject with the coerced path
273
424
  * @example
274
425
  * const capped = new TempDirectoryObject("myapp")
275
426
  * const file = capped.getFile("config.json")
276
427
  * console.log(file.path) // "/tmp/myapp-ABC123/config.json"
428
+ *
429
+ * @example
430
+ * // Absolute paths are relative to cap
431
+ * const abs = capped.getFile("/data/config.json")
432
+ * console.log(abs.path) // "/tmp/myapp-ABC123/data/config.json"
433
+ *
434
+ * @example
435
+ * // Excessive .. traversal clamps to cap
436
+ * const up = capped.getFile("../../../etc/passwd")
437
+ * console.log(up.path) // "/tmp/myapp-ABC123/passwd" (clamped to cap)
277
438
  */
278
439
  getFile(filename) {
279
440
  Valid.type(filename, "String")
280
441
 
281
- // Prevent absolute paths
442
+ // Fast path: if it's a simple filename (no separators, not absolute, no ..)
443
+ // use this as the parent directly
444
+ const isSimpleName = !path.isAbsolute(filename) &&
445
+ !filename.includes("/") &&
446
+ !filename.includes("\\") &&
447
+ !filename.includes("..")
448
+
449
+ if(isSimpleName) {
450
+ // Simple filename - create directly with this as parent
451
+ return new FileObject(filename, this)
452
+ }
453
+
454
+ // Complex path - handle coercion
455
+ const capResolved = path.resolve(this.#cap)
456
+ let targetPath
457
+
458
+ // If absolute, treat as relative to cap (virtual root)
282
459
  if(path.isAbsolute(filename)) {
283
- throw Sass.new("Absolute paths are not allowed in capped directories")
460
+ // Strip leading slashes to make relative
461
+ const relative = filename.replace(/^[/\\]+/, "")
462
+
463
+ // Join with cap
464
+ targetPath = path.join(capResolved, relative)
465
+ } else {
466
+ // Relative path - resolve from current directory
467
+ targetPath = FS.resolvePath(this.#realPath, filename)
284
468
  }
285
469
 
286
- // Prevent path traversal attacks
287
- const normalized = path.normalize(filename)
288
- if(normalized.includes("..")) {
289
- throw Sass.new("Path traversal (..) is not allowed in capped directories")
470
+ // Resolve to absolute path (handles .. and .)
471
+ const resolved = path.resolve(targetPath)
472
+
473
+ // Coerce: if path escaped cap, clamp to cap boundary
474
+ const coerced = resolved.startsWith(capResolved)
475
+ ? resolved
476
+ : capResolved
477
+
478
+ // Extract directory and filename parts
479
+ let fileDir = path.dirname(coerced)
480
+ let fileBasename = path.basename(coerced)
481
+
482
+ // Special case: if coerced is exactly the cap (file tried to escape),
483
+ // the file should be placed at the cap root with just the filename
484
+ if(coerced === capResolved) {
485
+ // Extract just the filename from the original path
486
+ fileBasename = path.basename(resolved)
487
+ fileDir = capResolved
290
488
  }
291
489
 
292
- // Pass the filename and this directory as parent
293
- // This ensures the FileObject maintains the correct parent reference
294
- return new FileObject(filename, this)
490
+ // Get or create the parent directory
491
+ const relativeToCap = path.relative(capResolved, fileDir)
492
+ const parentDir = !relativeToCap || relativeToCap === "."
493
+ ? this.#createCappedAtRoot()
494
+ : this.#buildDirectoryFromRelativePath(relativeToCap)
495
+
496
+ // Create FileObject with parent directory
497
+ return new FileObject(fileBasename, parentDir)
498
+ }
499
+
500
+ /**
501
+ * Override exists to use real filesystem path.
502
+ *
503
+ * @returns {Promise<boolean>} Whether the directory exists
504
+ */
505
+ get exists() {
506
+ return this.real.exists
507
+ }
508
+
509
+ /**
510
+ * Override read to use real filesystem path and return capped objects.
511
+ *
512
+ * @param {string} [pat=""] - Optional glob pattern
513
+ * @returns {Promise<{files: Array<FileObject>, directories: Array}>} Directory contents
514
+ */
515
+ async read(pat="") {
516
+ const {files, directories} = await this.real.read(pat)
517
+
518
+ // Convert plain DirectoryObjects to CappedDirectoryObjects with same cap
519
+ const cappedDirectories = directories.map(dir => {
520
+ const name = dir.name
521
+
522
+ return new this.constructor(name, this)
523
+ })
524
+
525
+ return {files, directories: cappedDirectories}
526
+ }
527
+
528
+ /**
529
+ * Override assureExists to use real filesystem path.
530
+ *
531
+ * @param {object} [options] - Options for mkdir
532
+ * @returns {Promise<void>}
533
+ */
534
+ async assureExists(options = {}) {
535
+ return await this.real.assureExists(options)
536
+ }
537
+
538
+ /**
539
+ * Override delete to use real filesystem path.
540
+ *
541
+ * @returns {Promise<void>}
542
+ */
543
+ async delete() {
544
+ return await this.real.delete()
545
+ }
546
+
547
+ /**
548
+ * Override remove to preserve temporary flag check.
549
+ *
550
+ * @returns {Promise<void>}
551
+ */
552
+ async remove() {
553
+ if(!this.temporary)
554
+ throw Sass.new("This is not a temporary directory.")
555
+
556
+ const {files, directories} = await this.read()
557
+
558
+ // Remove subdirectories recursively
559
+ for(const dir of directories)
560
+ await dir.remove()
561
+
562
+ // Remove files
563
+ for(const file of files)
564
+ await file.delete()
565
+
566
+ // Delete the now-empty directory
567
+ await this.delete()
295
568
  }
296
569
 
297
570
  /**
@@ -300,6 +573,6 @@ export default class CappedDirectoryObject extends DirectoryObject {
300
573
  * @returns {string} string representation of the CappedDirectoryObject
301
574
  */
302
575
  toString() {
303
- return `[CappedDirectoryObject: ${this.path}]`
576
+ return `[CappedDirectoryObject: ${this.path} (real: ${this.#realPath})]`
304
577
  }
305
578
  }
@@ -96,6 +96,7 @@ export default class FileObject extends FS {
96
96
  case "String":
97
97
  return new DirectoryObject(parent)
98
98
  case "DirectoryObject":
99
+ case "CappedDirectoryObject":
99
100
  case "TempDirectoryObject":
100
101
  return parent
101
102
  default:
@@ -103,7 +104,9 @@ export default class FileObject extends FS {
103
104
  }
104
105
  })()
105
106
 
106
- const final = FS.resolvePath(parentObject.path ?? ".", fixedFile)
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)
107
110
 
108
111
  const resolved = final
109
112
  const url = new URL(FS.pathToUri(resolved))
@@ -113,7 +116,9 @@ export default class FileObject extends FS {
113
116
 
114
117
  // If the file is directly in the provided parent directory, reuse that object
115
118
  // Otherwise, create a DirectoryObject for the actual parent directory
116
- const actualParent = parentObject && actualParentPath === parentObject.path
119
+ // Use real path for comparison if parent is capped
120
+ const parentRealPath = parentObject.realPath || parentObject.path
121
+ const actualParent = parentObject && actualParentPath === parentRealPath
117
122
  ? parentObject
118
123
  : new DirectoryObject(actualParentPath)
119
124
 
@@ -184,12 +189,28 @@ export default class FileObject extends FS {
184
189
  }
185
190
 
186
191
  /**
187
- * Return the fully resolved absolute path to the file on disk.
192
+ * 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.
194
+ * Use `.real.path` to always get the actual filesystem path.
188
195
  *
189
- * @returns {string} The fully resolved absolute file path
196
+ * @returns {string} The file path (virtual if parent is capped, real otherwise)
190
197
  */
191
198
  get path() {
192
- return this.#meta.path
199
+ const realPath = this.#meta.path
200
+ const parent = this.#meta.parent
201
+
202
+ // If parent is capped, return virtual path
203
+ if(parent?.capped) {
204
+ const cap = parent.cap
205
+ const capResolved = path.resolve(cap)
206
+ const relative = path.relative(capResolved, realPath)
207
+
208
+ // Return with leading slash to indicate it's cap-relative
209
+ return "/" + relative.split(path.sep).join("/")
210
+ }
211
+
212
+ // Otherwise return real path
213
+ return realPath
193
214
  }
194
215
 
195
216
  /**
@@ -264,6 +285,26 @@ export default class FileObject extends FS {
264
285
  return this.#meta.parent
265
286
  }
266
287
 
288
+ /**
289
+ * Returns a plain FileObject representing the actual filesystem location.
290
+ * This provides an "escape hatch" when working with capped directories,
291
+ * allowing direct filesystem access when needed.
292
+ *
293
+ * @returns {FileObject} Uncapped file object at the real filesystem path
294
+ * @example
295
+ * const temp = new TempDirectoryObject("myapp")
296
+ * const file = temp.getFile("/config/app.json")
297
+ *
298
+ * // file.path shows virtual path
299
+ * console.log(file.path) // "/config/app.json"
300
+ * // file.real.path shows actual filesystem path
301
+ * console.log(file.real.path) // "/tmp/myapp-ABC123/config/app.json"
302
+ * file.real.parent.parent // Can traverse outside the cap
303
+ */
304
+ get real() {
305
+ return new FileObject(this.#meta.path)
306
+ }
307
+
267
308
  /**
268
309
  * Check if a file can be read. Returns true if the file can be read, false
269
310
  *
@@ -271,7 +312,7 @@ export default class FileObject extends FS {
271
312
  */
272
313
  async canRead() {
273
314
  try {
274
- await fs.access(this.path, fs.constants.R_OK)
315
+ await fs.access(this.#meta.path, fs.constants.R_OK)
275
316
 
276
317
  return true
277
318
  } catch(_) {
@@ -286,7 +327,7 @@ export default class FileObject extends FS {
286
327
  */
287
328
  async canWrite() {
288
329
  try {
289
- await fs.access(this.path, fs.constants.W_OK)
330
+ await fs.access(this.#meta.path, fs.constants.W_OK)
290
331
 
291
332
  return true
292
333
  } catch(_) {
@@ -301,7 +342,7 @@ export default class FileObject extends FS {
301
342
  */
302
343
  async #fileExists() {
303
344
  try {
304
- await fs.access(this.path, fs.constants.F_OK)
345
+ await fs.access(this.#meta.path, fs.constants.F_OK)
305
346
 
306
347
  return true
307
348
  } catch(_) {
@@ -316,7 +357,7 @@ export default class FileObject extends FS {
316
357
  */
317
358
  async size() {
318
359
  try {
319
- const stat = await fs.stat(this.path)
360
+ const stat = await fs.stat(this.#meta.path)
320
361
 
321
362
  return stat.size
322
363
  } catch(_) {
@@ -332,7 +373,7 @@ export default class FileObject extends FS {
332
373
  */
333
374
  async modified() {
334
375
  try {
335
- const stat = await fs.stat(this.path)
376
+ const stat = await fs.stat(this.#meta.path)
336
377
 
337
378
  return stat.mtime
338
379
  } catch(_) {
@@ -6,6 +6,7 @@
6
6
 
7
7
  import fs from "node:fs"
8
8
  import os from "node:os"
9
+ import path from "node:path"
9
10
 
10
11
  import CappedDirectoryObject from "./CappedDirectoryObject.js"
11
12
  import Sass from "./Sass.js"
@@ -57,22 +58,63 @@ export default class TempDirectoryObject extends CappedDirectoryObject {
57
58
  * await parent.remove() // Removes both parent and child
58
59
  */
59
60
  constructor(name, parent=null) {
60
- let finalName = name
61
+ let dirPath
62
+ let cappedParent = parent
61
63
 
62
- // Only generate unique suffix if we have a name and no parent
63
- if(name && !parent) {
64
- const prefix = name.endsWith("-") ? name : `${name}-`
65
- const uniqueSuffix =
66
- Math.random()
67
- .toString(36)
68
- .substring(2, 8)
69
- .toUpperCase()
70
- finalName = `${prefix}${uniqueSuffix}`
64
+ if(!parent) {
65
+ // No parent: need to create a capped parent at tmpdir first
66
+ cappedParent = new CappedDirectoryObject(os.tmpdir(), null, true)
67
+
68
+ if(name) {
69
+ // Check if name is a simple name (no separators, not absolute)
70
+ const isSimpleName = !path.isAbsolute(name) &&
71
+ !name.includes("/") &&
72
+ !name.includes("\\") &&
73
+ !name.includes(path.sep)
74
+
75
+ if(isSimpleName) {
76
+ // Simple name: add unique suffix
77
+ const prefix = name.endsWith("-") ? name : `${name}-`
78
+ const uniqueSuffix =
79
+ Math.random()
80
+ .toString(36)
81
+ .substring(2, 8)
82
+ .toUpperCase()
83
+ dirPath = `${prefix}${uniqueSuffix}`
84
+ } else {
85
+ // Complex path: use as-is, let CappedDirectoryObject handle coercion
86
+ dirPath = name
87
+ }
88
+ } else {
89
+ // No name: use tmpdir itself (no parent)
90
+ dirPath = os.tmpdir()
91
+ cappedParent = null
92
+ }
93
+ } else {
94
+ // With parent: validate it's a proper temp directory parent
95
+ if(!(parent instanceof CappedDirectoryObject)) {
96
+ throw Sass.new(
97
+ "Parent must be a CappedDirectoryObject or TempDirectoryObject."
98
+ )
99
+ }
100
+
101
+ // SECURITY: Ensure parent's cap is tmpdir (prevent escape to other caps)
102
+ const tmpdir = os.tmpdir()
103
+ if(parent.cap !== tmpdir) {
104
+ throw Sass.new(
105
+ `Parent must be capped to OS temp directory (${tmpdir}), ` +
106
+ `got cap: ${parent.cap}`
107
+ )
108
+ }
109
+
110
+ dirPath = name || ""
111
+ if(!dirPath) {
112
+ throw Sass.new("Name must not be empty when parent is provided.")
113
+ }
71
114
  }
72
115
 
73
- // Call parent constructor with the cap set to OS temp directory
74
- // Mark as temporary=true so remove() works
75
- super(finalName, os.tmpdir(), parent, true)
116
+ // Call parent constructor with new signature
117
+ super(dirPath, cappedParent, true)
76
118
 
77
119
  // Temp-specific behavior: create directory immediately
78
120
  this.#createDirectory()
@@ -86,12 +128,13 @@ export default class TempDirectoryObject extends CappedDirectoryObject {
86
128
  */
87
129
  #createDirectory() {
88
130
  try {
89
- fs.mkdirSync(this.path)
131
+ // Use recursive: true to create parent directories as needed
132
+ fs.mkdirSync(this.realPath, {recursive: true})
90
133
  } catch(e) {
91
134
  // EEXIST is fine - directory already exists
92
135
  if(e.code !== "EEXIST") {
93
136
  throw Sass.new(
94
- `Unable to create temporary directory '${this.path}': ${e.message}`
137
+ `Unable to create temporary directory '${this.realPath}': ${e.message}`
95
138
  )
96
139
  }
97
140
  }
@@ -11,21 +11,32 @@ export default class CappedDirectoryObject extends DirectoryObject {
11
11
  /**
12
12
  * Constructs a CappedDirectoryObject instance.
13
13
  *
14
- * This is an abstract base class - use subclasses like TempDirectoryObject
15
- * that define specific caps.
14
+ * Without a parent, the path becomes both the directory location and the cap
15
+ * (virtual root). With a parent, the path is resolved relative to the parent's
16
+ * cap using virtual path semantics (absolute paths treated as cap-relative).
16
17
  *
17
- * @param {string?} name - Base name for the directory (if empty/null, uses cap root)
18
- * @param {string} cap - The root path that constrains this directory tree
18
+ * @param {string} dirPath - Directory path (becomes cap if no parent, else relative to parent's cap)
19
19
  * @param {CappedDirectoryObject?} [parent] - Optional parent capped directory
20
20
  * @param {boolean} [temporary=false] - Whether this is a temporary directory
21
- * @throws {Sass} If name is absolute
22
- * @throws {Sass} If name is empty (when parent is provided)
23
- * @throws {Sass} If name contains path separators
24
- * @throws {Sass} If parent is not a capped directory
25
- * @throws {Sass} If parent's lineage does not trace back to the cap
21
+ * @throws {Sass} If path is empty
22
+ * @throws {Sass} If parent is provided but not a CappedDirectoryObject
26
23
  * @throws {Sass} If the resulting path would escape the cap
24
+ * @example
25
+ * // Create new capped directory
26
+ * const cache = new CappedDirectoryObject("/home/user/.cache")
27
+ * // path: /home/user/.cache, cap: /home/user/.cache
28
+ *
29
+ * @example
30
+ * // Create subdirectory with parent
31
+ * const data = new CappedDirectoryObject("data", cache)
32
+ * // path: /home/user/.cache/data, cap: /home/user/.cache
33
+ *
34
+ * @example
35
+ * // Virtual absolute path with parent
36
+ * const config = new CappedDirectoryObject("/etc/config", cache)
37
+ * // path: /home/user/.cache/etc/config, cap: /home/user/.cache
27
38
  */
28
- constructor(name: string | null, cap: string, parent?: CappedDirectoryObject | null, temporary?: boolean);
39
+ constructor(dirPath: string, parent?: CappedDirectoryObject | null, temporary?: boolean);
29
40
  /**
30
41
  * Returns the cap path for this directory.
31
42
  *
@@ -38,6 +49,32 @@ export default class CappedDirectoryObject extends DirectoryObject {
38
49
  * @returns {boolean} Always true for CappedDirectoryObject instances
39
50
  */
40
51
  get capped(): boolean;
52
+ /**
53
+ * Returns the real filesystem path (for internal and subclass use).
54
+ *
55
+ * @protected
56
+ * @returns {string} The actual filesystem path
57
+ */
58
+ protected get realPath(): string;
59
+ /**
60
+ * Returns a plain DirectoryObject representing the actual filesystem location.
61
+ * This provides an "escape hatch" from the capped environment to interact
62
+ * with the real filesystem when needed.
63
+ *
64
+ * @returns {DirectoryObject} Uncapped directory object at the real filesystem path
65
+ * @example
66
+ * const temp = new TempDirectoryObject("myapp")
67
+ * const subdir = temp.getDirectory("data")
68
+ *
69
+ * // Work within the capped environment (virtual paths)
70
+ * console.log(subdir.path) // "/data" (virtual)
71
+ * subdir.getFile("config.json") // Stays within cap
72
+ *
73
+ * // Break out to real filesystem when needed
74
+ * console.log(subdir.real.path) // "/tmp/myapp-ABC123/data" (real)
75
+ * subdir.real.parent // Can traverse outside the cap
76
+ */
77
+ get real(): DirectoryObject;
41
78
  /**
42
79
  * Returns a generator that walks up to the cap.
43
80
  *
@@ -47,20 +84,41 @@ export default class CappedDirectoryObject extends DirectoryObject {
47
84
  /**
48
85
  * Creates a new CappedDirectoryObject by extending this directory's path.
49
86
  *
50
- * Validates that the resulting path remains within the cap directory tree.
87
+ * All paths are coerced to remain within the cap directory tree:
88
+ * - Absolute paths (e.g., "/foo") are treated as relative to the cap
89
+ * - Parent traversal ("..") is allowed but clamped at the cap boundary
90
+ * - The cap acts as the virtual root directory
51
91
  *
52
- * @param {string} newPath - The path segment to append
53
- * @returns {CappedDirectoryObject} A new CappedDirectoryObject with the extended path
54
- * @throws {Sass} If the path would escape the cap directory
55
- * @throws {Sass} If the path is absolute
56
- * @throws {Sass} If the path contains traversal (..)
92
+ * @param {string} newPath - The path to resolve (can be absolute or contain ..)
93
+ * @returns {CappedDirectoryObject} A new CappedDirectoryObject with the coerced path
57
94
  * @example
58
95
  * const capped = new TempDirectoryObject("myapp")
59
96
  * const subDir = capped.getDirectory("data")
60
97
  * console.log(subDir.path) // "/tmp/myapp-ABC123/data"
98
+ *
99
+ * @example
100
+ * // Absolute paths are relative to cap
101
+ * const abs = capped.getDirectory("/foo/bar")
102
+ * console.log(abs.path) // "/tmp/myapp-ABC123/foo/bar"
103
+ *
104
+ * @example
105
+ * // Excessive .. traversal clamps to cap
106
+ * const up = capped.getDirectory("../../../etc/passwd")
107
+ * console.log(up.path) // "/tmp/myapp-ABC123" (clamped to cap)
61
108
  */
62
109
  getDirectory(newPath: string): CappedDirectoryObject;
110
+ /**
111
+ * Override read to use real filesystem path and return capped objects.
112
+ *
113
+ * @param {string} [pat=""] - Optional glob pattern
114
+ * @returns {Promise<{files: Array<FileObject>, directories: Array}>} Directory contents
115
+ */
116
+ read(pat?: string): Promise<{
117
+ files: Array<FileObject>;
118
+ directories: any[];
119
+ }>;
63
120
  #private;
64
121
  }
65
122
  import DirectoryObject from "./DirectoryObject.js";
123
+ import FileObject from "./FileObject.js";
66
124
  //# sourceMappingURL=CappedDirectoryObject.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CappedDirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/CappedDirectoryObject.js"],"names":[],"mappings":"AAiBA;;;;;;;;GAQG;AACH;IAGE;;;;;;;;;;;;;;;;OAgBG;IACH,kBAXW,MAAM,OAAC,OACP,MAAM,WACN,qBAAqB,OAAC,cACtB,OAAO,EAkFjB;IAqBD;;;;OAIG;IACH,WAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,cAFa,OAAO,CAInB;IA8DD;;;;OAIG;IACH,cAFa,SAAS,CAAC,eAAe,CAAC,CAItC;IAED;;;;;;;;;;;;;;OAcG;IACH,sBAVW,MAAM,GACJ,qBAAqB,CA0BjC;;CA4CF;4BAnS2B,sBAAsB"}
1
+ {"version":3,"file":"CappedDirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/CappedDirectoryObject.js"],"names":[],"mappings":"AAkBA;;;;;;;;GAQG;AACH;IAGE;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,qBArBW,MAAM,WACN,qBAAqB,OAAC,cACtB,OAAO,EA0EjB;IAqBD;;;;OAIG;IACH,WAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,cAFa,OAAO,CAInB;IAED;;;;;OAKG;IACH,0BAFa,MAAM,CAIlB;IAqCD;;;;;;;;;;;;;;;;;OAiBG;IACH,YAba,eAAe,CAe3B;IA2ED;;;;OAIG;IACH,cAFa,SAAS,CAAC,eAAe,CAAC,CAItC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,sBAjBW,MAAM,GACJ,qBAAqB,CAiEjC;IAqID;;;;;OAKG;IACH,WAHW,MAAM,GACJ,OAAO,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAAC,WAAW,QAAO;KAAC,CAAC,CAanE;;CAoDF;4BApjB2B,sBAAsB;uBAC3B,iBAAiB"}
@@ -50,9 +50,11 @@ export default class FileObject extends FS {
50
50
  */
51
51
  get supplied(): string;
52
52
  /**
53
- * Return the fully resolved absolute path to the file on disk.
53
+ * Returns the file path. If the parent is a capped directory, returns the
54
+ * virtual path relative to the cap. Otherwise returns the real filesystem path.
55
+ * Use `.real.path` to always get the actual filesystem path.
54
56
  *
55
- * @returns {string} The fully resolved absolute file path
57
+ * @returns {string} The file path (virtual if parent is capped, real otherwise)
56
58
  */
57
59
  get path(): string;
58
60
  /**
@@ -107,6 +109,23 @@ export default class FileObject extends FS {
107
109
  * @returns {DirectoryObject} The parent directory object
108
110
  */
109
111
  get parent(): DirectoryObject;
112
+ /**
113
+ * Returns a plain FileObject representing the actual filesystem location.
114
+ * This provides an "escape hatch" when working with capped directories,
115
+ * allowing direct filesystem access when needed.
116
+ *
117
+ * @returns {FileObject} Uncapped file object at the real filesystem path
118
+ * @example
119
+ * const temp = new TempDirectoryObject("myapp")
120
+ * const file = temp.getFile("/config/app.json")
121
+ *
122
+ * // file.path shows virtual path
123
+ * console.log(file.path) // "/config/app.json"
124
+ * // file.real.path shows actual filesystem path
125
+ * console.log(file.real.path) // "/tmp/myapp-ABC123/config/app.json"
126
+ * file.real.parent.parent // Can traverse outside the cap
127
+ */
128
+ get real(): FileObject;
110
129
  /**
111
130
  * Check if a file can be read. Returns true if the file can be read, false
112
131
  *
@@ -1 +1 @@
1
- {"version":3,"file":"FileObject.d.ts","sourceRoot":"","sources":["../../lib/FileObject.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;GAcG;AAEH;uBA8He,MAAM;IA7HnB;;;;;OAKG;IACH,yBAFU;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,OAAO,KAAK,GAAG,OAAO,IAAI,CAAC,CAAA;KAAC,CAO1D;IA2BF;;;;;OAKG;IACH,sBAHW,MAAM,GAAG,UAAU,WACnB,eAAe,GAAC,MAAM,GAAC,IAAI,EAkDrC;IAWD;;;;OAIG;IACH,UAFa,MAAM,CAclB;IAWD;;;;OAIG;IACH,cAFa,OAAO,CAAC,OAAO,CAAC,CAI5B;IAED;;;;OAIG;IACH,gBAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,YAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,WAFa,GAAG,CAIf;IAED;;;;OAIG;IACH,YAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,cAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,iBAFa,MAAM,CAIlB;IACD;;;;OAIG;IACH,cAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,mBAFa,OAAO,CAInB;IAED;;;;;;;;;;;;;;OAcG;IACH,cAFa,eAAe,CAI3B;IAED;;;;OAIG;IACH,WAFa,OAAO,CAAC,OAAO,CAAC,CAU5B;IAED;;;;OAIG;IACH,YAFa,OAAO,CAAC,OAAO,CAAC,CAU5B;IAiBD;;;;OAIG;IACH,QAFa,OAAO,CAAC,MAAM,OAAC,CAAC,CAU5B;IAED;;;;;OAKG;IACH,YAFa,OAAO,CAAC,IAAI,OAAC,CAAC,CAU1B;IAsBD;;;;;OAKG;IACH,gBAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAY3B;IAED;;;;;;;;;;;OAWG;IACH,cARa,OAAO,CAAC,MAAM,CAAC,CAkB3B;IAED;;;;;;;;;;;OAWG;IACH,eARW,MAAM,aACN,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAezB;IAED;;;;;;;;;;;;;;;OAeG;IACH,kBAXW,WAAW,GAAC,IAAI,GAAC,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAyBzB;IAED;;;;;;;;;;;;;;OAcG;IACH,gBAXW,MAAM,aACN,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAkC5B;IAED;;;;OAIG;IACH,UAFa,OAAO,CAAC,MAAM,CAAC,CAY3B;IAED;;;;;;;;;OASG;IACH,UAPa,OAAO,CAAC,IAAI,CAAC,CAiBzB;;CACF;eA7gBc,SAAS;4BADI,sBAAsB;kBARhC,OAAO;iBAIR,MAAM"}
1
+ {"version":3,"file":"FileObject.d.ts","sourceRoot":"","sources":["../../lib/FileObject.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;GAcG;AAEH;uBAmIe,MAAM;IAlInB;;;;;OAKG;IACH,yBAFU;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,KAAK,CAAC,OAAO,KAAK,GAAG,OAAO,IAAI,CAAC,CAAA;KAAC,CAO1D;IA2BF;;;;;OAKG;IACH,sBAHW,MAAM,GAAG,UAAU,WACnB,eAAe,GAAC,MAAM,GAAC,IAAI,EAuDrC;IAWD;;;;OAIG;IACH,UAFa,MAAM,CAclB;IAWD;;;;OAIG;IACH,cAFa,OAAO,CAAC,OAAO,CAAC,CAI5B;IAED;;;;OAIG;IACH,gBAFa,MAAM,CAIlB;IAED;;;;;;OAMG;IACH,YAFa,MAAM,CAkBlB;IAED;;;;OAIG;IACH,WAFa,GAAG,CAIf;IAED;;;;OAIG;IACH,YAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,cAFa,MAAM,CAIlB;IAED;;;;OAIG;IACH,iBAFa,MAAM,CAIlB;IACD;;;;OAIG;IACH,cAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,mBAFa,OAAO,CAInB;IAED;;;;;;;;;;;;;;OAcG;IACH,cAFa,eAAe,CAI3B;IAED;;;;;;;;;;;;;;;OAeG;IACH,YAXa,UAAU,CAatB;IAED;;;;OAIG;IACH,WAFa,OAAO,CAAC,OAAO,CAAC,CAU5B;IAED;;;;OAIG;IACH,YAFa,OAAO,CAAC,OAAO,CAAC,CAU5B;IAiBD;;;;OAIG;IACH,QAFa,OAAO,CAAC,MAAM,OAAC,CAAC,CAU5B;IAED;;;;;OAKG;IACH,YAFa,OAAO,CAAC,IAAI,OAAC,CAAC,CAU1B;IAsBD;;;;;OAKG;IACH,gBAHW,MAAM,GACJ,OAAO,CAAC,MAAM,CAAC,CAY3B;IAED;;;;;;;;;;;OAWG;IACH,cARa,OAAO,CAAC,MAAM,CAAC,CAkB3B;IAED;;;;;;;;;;;OAWG;IACH,eARW,MAAM,aACN,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAezB;IAED;;;;;;;;;;;;;;;OAeG;IACH,kBAXW,WAAW,GAAC,IAAI,GAAC,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC,CAyBzB;IAED;;;;;;;;;;;;;;OAcG;IACH,gBAXW,MAAM,aACN,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAkC5B;IAED;;;;OAIG;IACH,UAFa,OAAO,CAAC,MAAM,CAAC,CAY3B;IAED;;;;;;;;;OASG;IACH,UAPa,OAAO,CAAC,IAAI,CAAC,CAiBzB;;CACF;eAtjBc,SAAS;4BADI,sBAAsB;kBARhC,OAAO;iBAIR,MAAM"}
@@ -1 +1 @@
1
- {"version":3,"file":"TempDirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/TempDirectoryObject.js"],"names":[],"mappings":"AAYA;;;;;;;;;GASG;AACH;IAEE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,mBAxBW,MAAM,OAAC,WACP,mBAAmB,OAAC,EA2C9B;IAqBD;;;;;;;;;;;;;;OAcG;IACH,sBAVW,MAAM,GACJ,mBAAmB,CAY/B;IAED;;;;;;;;;;;;;;OAcG;IACH,kBAVW,MAAM,GACJ,UAAU,CAYtB;;CAUF;kCA1IiC,4BAA4B"}
1
+ {"version":3,"file":"TempDirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/TempDirectoryObject.js"],"names":[],"mappings":"AAaA;;;;;;;;;GASG;AACH;IAEE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,mBAxBW,MAAM,OAAC,WACP,mBAAmB,OAAC,EAoF9B;IAsBD;;;;;;;;;;;;;;OAcG;IACH,sBAVW,MAAM,GACJ,mBAAmB,CAY/B;IAED;;;;;;;;;;;;;;OAcG;IACH,kBAVW,MAAM,GACJ,UAAU,CAYtB;;CAUF;kCApLiC,4BAA4B"}