@gesslar/toolkit 1.9.1 → 1.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 +1 -1
- package/src/index.js +2 -0
- package/src/lib/CappedDirectoryObject.js +237 -0
- package/src/lib/DirectoryObject.js +31 -10
- package/src/lib/FS.js +0 -87
- package/src/lib/FileObject.js +1 -0
- package/src/lib/TempDirectoryObject.js +148 -0
- package/src/types/index.d.ts +2 -0
- package/src/types/lib/CappedDirectoryObject.d.ts +60 -0
- package/src/types/lib/CappedDirectoryObject.d.ts.map +1 -0
- package/src/types/lib/DirectoryObject.d.ts +20 -7
- package/src/types/lib/DirectoryObject.d.ts.map +1 -1
- package/src/types/lib/FS.d.ts +0 -40
- package/src/types/lib/FS.d.ts.map +1 -1
- package/src/types/lib/FileObject.d.ts.map +1 -1
- package/src/types/lib/TempDirectoryObject.d.ts +82 -0
- package/src/types/lib/TempDirectoryObject.d.ts.map +1 -0
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -13,8 +13,10 @@ export {default as Util} from "./lib/Util.js"
|
|
|
13
13
|
|
|
14
14
|
// Node-specific exports
|
|
15
15
|
export {default as Cache} from "./lib/Cache.js"
|
|
16
|
+
export {default as CappedDirectoryObject} from "./lib/CappedDirectoryObject.js"
|
|
16
17
|
export {default as Contract} from "./lib/Contract.js"
|
|
17
18
|
export {default as DirectoryObject} from "./lib/DirectoryObject.js"
|
|
19
|
+
export {default as TempDirectoryObject} from "./lib/TempDirectoryObject.js"
|
|
18
20
|
export {default as FileObject} from "./lib/FileObject.js"
|
|
19
21
|
export {default as FS} from "./lib/FS.js"
|
|
20
22
|
export {default as Glog} from "./lib/Glog.js"
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file CappedDirectoryObject.js
|
|
3
|
+
* @description Abstract base class for directory objects that are constrained
|
|
4
|
+
* to a specific directory tree (the "cap"). This provides security by ensuring
|
|
5
|
+
* all operations remain within the capped directory hierarchy.
|
|
6
|
+
*
|
|
7
|
+
* This class is not intended to be instantiated directly. Use subclasses like
|
|
8
|
+
* TempDirectoryObject that define specific caps.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import path from "node:path"
|
|
12
|
+
|
|
13
|
+
import {Data, Valid} from "../browser/index.js"
|
|
14
|
+
import DirectoryObject from "./DirectoryObject.js"
|
|
15
|
+
import FileObject from "./FileObject.js"
|
|
16
|
+
import Sass from "./Sass.js"
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* CappedDirectoryObject extends DirectoryObject with constraints that ensure
|
|
20
|
+
* all operations are restricted to a specific directory tree (the "cap").
|
|
21
|
+
*
|
|
22
|
+
* All path operations are validated to ensure they remain within the
|
|
23
|
+
* cap directory hierarchy for security.
|
|
24
|
+
*
|
|
25
|
+
* @augments DirectoryObject
|
|
26
|
+
*/
|
|
27
|
+
export default class CappedDirectoryObject extends DirectoryObject {
|
|
28
|
+
#cap
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Constructs a CappedDirectoryObject instance.
|
|
32
|
+
*
|
|
33
|
+
* This is an abstract base class - use subclasses like TempDirectoryObject
|
|
34
|
+
* that define specific caps.
|
|
35
|
+
*
|
|
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 {CappedDirectoryObject?} [parent] - Optional parent capped directory
|
|
39
|
+
* @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
|
|
45
|
+
* @throws {Sass} If the resulting path would escape the cap
|
|
46
|
+
*/
|
|
47
|
+
constructor(name, cap, parent=null, temporary=false) {
|
|
48
|
+
Valid.type(cap, "String")
|
|
49
|
+
|
|
50
|
+
// Validate parent using instanceof since TypeSpec doesn't understand inheritance
|
|
51
|
+
if(parent !== null && !(parent instanceof CappedDirectoryObject)) {
|
|
52
|
+
throw Sass.new(`Parent must be null or a CappedDirectoryObject instance, got ${Data.typeOf(parent)}`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
let dirPath
|
|
56
|
+
|
|
57
|
+
// Special case: empty name with no parent means use cap root
|
|
58
|
+
if(!name && !parent) {
|
|
59
|
+
dirPath = cap
|
|
60
|
+
} else {
|
|
61
|
+
Valid.type(name, "String")
|
|
62
|
+
|
|
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
|
+
)
|
|
76
|
+
|
|
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)
|
|
106
|
+
} else {
|
|
107
|
+
// No parent - this is a root-level capped directory
|
|
108
|
+
dirPath = path.join(cap, name)
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Call parent constructor with the path
|
|
113
|
+
// Pass through the temporary flag (subclasses control this)
|
|
114
|
+
super(dirPath, temporary)
|
|
115
|
+
|
|
116
|
+
// Store the cap AFTER calling super()
|
|
117
|
+
this.#cap = cap
|
|
118
|
+
|
|
119
|
+
// Validate that this path is within the cap
|
|
120
|
+
this.#validateCapPath()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Validates that the directory path is within the cap directory tree.
|
|
125
|
+
*
|
|
126
|
+
* @private
|
|
127
|
+
* @throws {Sass} If the path is not within the cap directory
|
|
128
|
+
*/
|
|
129
|
+
#validateCapPath() {
|
|
130
|
+
const cap = this.#cap
|
|
131
|
+
const resolved = path.resolve(this.path)
|
|
132
|
+
const capResolved = path.resolve(cap)
|
|
133
|
+
|
|
134
|
+
// Check if the resolved path starts with the cap directory
|
|
135
|
+
if(!resolved.startsWith(capResolved)) {
|
|
136
|
+
throw Sass.new(
|
|
137
|
+
`Path '${this.path}' is not within the cap directory '${cap}'`
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Returns the cap path for this directory.
|
|
144
|
+
*
|
|
145
|
+
* @returns {string} The cap directory path
|
|
146
|
+
*/
|
|
147
|
+
get cap() {
|
|
148
|
+
return this.#cap
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Returns whether this directory is capped.
|
|
153
|
+
*
|
|
154
|
+
* @returns {boolean} Always true for CappedDirectoryObject instances
|
|
155
|
+
*/
|
|
156
|
+
get capped() {
|
|
157
|
+
return true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Creates a new CappedDirectoryObject by extending this directory's path.
|
|
162
|
+
*
|
|
163
|
+
* Validates that the resulting path remains within the cap directory tree.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} newPath - The path segment to append
|
|
166
|
+
* @returns {CappedDirectoryObject} A new CappedDirectoryObject with the extended path
|
|
167
|
+
* @throws {Sass} If the path would escape the cap directory
|
|
168
|
+
* @throws {Sass} If the path is absolute
|
|
169
|
+
* @throws {Sass} If the path contains traversal (..)
|
|
170
|
+
* @example
|
|
171
|
+
* const capped = new TempDirectoryObject("myapp")
|
|
172
|
+
* const subDir = capped.addDirectory("data")
|
|
173
|
+
* console.log(subDir.path) // "/tmp/myapp-ABC123/data"
|
|
174
|
+
*/
|
|
175
|
+
addDirectory(newPath) {
|
|
176
|
+
Valid.type(newPath, "String")
|
|
177
|
+
|
|
178
|
+
// Prevent absolute paths
|
|
179
|
+
if(path.isAbsolute(newPath)) {
|
|
180
|
+
throw Sass.new("Absolute paths are not allowed in capped directories")
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Prevent path traversal attacks
|
|
184
|
+
const normalized = path.normalize(newPath)
|
|
185
|
+
if(normalized.includes("..")) {
|
|
186
|
+
throw Sass.new("Path traversal (..) is not allowed in capped directories")
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Use the constructor of the current class (supports subclassing)
|
|
190
|
+
// Pass this as parent so the child inherits the same cap
|
|
191
|
+
return new this.constructor(newPath, this)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Creates a new FileObject by extending this directory's path.
|
|
196
|
+
*
|
|
197
|
+
* Validates that the resulting path remains within the cap directory tree.
|
|
198
|
+
*
|
|
199
|
+
* @param {string} filename - The filename to append
|
|
200
|
+
* @returns {FileObject} A new FileObject with the extended path
|
|
201
|
+
* @throws {Sass} If the path would escape the cap directory
|
|
202
|
+
* @throws {Sass} If the path is absolute
|
|
203
|
+
* @throws {Sass} If the path contains traversal (..)
|
|
204
|
+
* @example
|
|
205
|
+
* const capped = new TempDirectoryObject("myapp")
|
|
206
|
+
* const file = capped.addFile("config.json")
|
|
207
|
+
* console.log(file.path) // "/tmp/myapp-ABC123/config.json"
|
|
208
|
+
*/
|
|
209
|
+
addFile(filename) {
|
|
210
|
+
Valid.type(filename, "String")
|
|
211
|
+
|
|
212
|
+
// Prevent absolute paths
|
|
213
|
+
if(path.isAbsolute(filename)) {
|
|
214
|
+
throw Sass.new("Absolute paths are not allowed in capped directories")
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Prevent path traversal attacks
|
|
218
|
+
const normalized = path.normalize(filename)
|
|
219
|
+
if(normalized.includes("..")) {
|
|
220
|
+
throw Sass.new("Path traversal (..) is not allowed in capped directories")
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Create the file path by joining this directory with the filename
|
|
224
|
+
const filePath = path.join(this.path, filename)
|
|
225
|
+
|
|
226
|
+
return new FileObject(filePath)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Returns a string representation of the CappedDirectoryObject.
|
|
231
|
+
*
|
|
232
|
+
* @returns {string} string representation of the CappedDirectoryObject
|
|
233
|
+
*/
|
|
234
|
+
toString() {
|
|
235
|
+
return `[CappedDirectoryObject: ${this.path}]`
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -9,10 +9,10 @@ import path from "node:path"
|
|
|
9
9
|
import {URL} from "node:url"
|
|
10
10
|
import util from "node:util"
|
|
11
11
|
|
|
12
|
+
import {Data, Valid} from "../browser/index.js"
|
|
12
13
|
import FS from "./FS.js"
|
|
13
14
|
import FileObject from "./FileObject.js"
|
|
14
15
|
import Sass from "./Sass.js"
|
|
15
|
-
import {Data, Valid} from "../browser/index.js"
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* DirectoryObject encapsulates metadata and operations for a directory,
|
|
@@ -64,10 +64,10 @@ export default class DirectoryObject extends FS {
|
|
|
64
64
|
constructor(directory=null, temporary=false) {
|
|
65
65
|
super()
|
|
66
66
|
|
|
67
|
-
Valid.type(directory, "String|DirectoryObject|Null")
|
|
67
|
+
Valid.type(directory, "String|TempDirectoryObject|DirectoryObject|Null")
|
|
68
68
|
|
|
69
69
|
// If passed a DirectoryObject, extract its path
|
|
70
|
-
if(Data.isType(directory, "DirectoryObject"))
|
|
70
|
+
if(Data.isType(directory, "DirectoryObject") || Data.isType(directory, "TempDirectoryObject"))
|
|
71
71
|
directory = directory.path
|
|
72
72
|
|
|
73
73
|
const fixedDir = FS.fixSlashes(directory ?? ".")
|
|
@@ -231,7 +231,8 @@ export default class DirectoryObject extends FS {
|
|
|
231
231
|
* @throws {Sass} If the directory is not marked as temporary
|
|
232
232
|
* @throws {Sass} If the directory deletion fails
|
|
233
233
|
* @example
|
|
234
|
-
* const tempDir =
|
|
234
|
+
* const tempDir = new TempDirectoryObject("my-temp")
|
|
235
|
+
* await tempDir.assureExists()
|
|
235
236
|
* // ... use the directory ...
|
|
236
237
|
* await tempDir.remove() // Recursively deletes everything
|
|
237
238
|
*/
|
|
@@ -440,20 +441,19 @@ export default class DirectoryObject extends FS {
|
|
|
440
441
|
}
|
|
441
442
|
|
|
442
443
|
/**
|
|
443
|
-
* Creates a new DirectoryObject by
|
|
444
|
-
* path.
|
|
444
|
+
* Creates a new DirectoryObject by extending this directory's path.
|
|
445
445
|
*
|
|
446
446
|
* Uses overlapping path segment detection to intelligently combine paths.
|
|
447
447
|
* Preserves the temporary flag from the current directory.
|
|
448
448
|
*
|
|
449
|
-
* @param {string} newPath - The path to
|
|
450
|
-
* @returns {DirectoryObject} A new DirectoryObject with the
|
|
449
|
+
* @param {string} newPath - The path to append to this directory's path.
|
|
450
|
+
* @returns {DirectoryObject} A new DirectoryObject with the extended path.
|
|
451
451
|
* @example
|
|
452
452
|
* const dir = new DirectoryObject("/projects/git/toolkit")
|
|
453
|
-
* const subDir = dir.
|
|
453
|
+
* const subDir = dir.addDirectory("src/lib")
|
|
454
454
|
* console.log(subDir.path) // "/projects/git/toolkit/src/lib"
|
|
455
455
|
*/
|
|
456
|
-
|
|
456
|
+
addDirectory(newPath) {
|
|
457
457
|
Valid.type(newPath, "String")
|
|
458
458
|
|
|
459
459
|
const thisPath = this.path
|
|
@@ -461,4 +461,25 @@ export default class DirectoryObject extends FS {
|
|
|
461
461
|
|
|
462
462
|
return new this.constructor(merged, this.temporary)
|
|
463
463
|
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Creates a new FileObject by extending this directory's path.
|
|
467
|
+
*
|
|
468
|
+
* Uses overlapping path segment detection to intelligently combine paths.
|
|
469
|
+
*
|
|
470
|
+
* @param {string} filename - The filename to append to this directory's path.
|
|
471
|
+
* @returns {FileObject} A new FileObject with the extended path.
|
|
472
|
+
* @example
|
|
473
|
+
* const dir = new DirectoryObject("/projects/git/toolkit")
|
|
474
|
+
* const file = dir.addFile("package.json")
|
|
475
|
+
* console.log(file.path) // "/projects/git/toolkit/package.json"
|
|
476
|
+
*/
|
|
477
|
+
addFile(filename) {
|
|
478
|
+
Valid.type(filename, "String")
|
|
479
|
+
|
|
480
|
+
const thisPath = this.path
|
|
481
|
+
const merged = FS.mergeOverlappingPaths(thisPath, filename)
|
|
482
|
+
|
|
483
|
+
return new FileObject(merged)
|
|
484
|
+
}
|
|
464
485
|
}
|
package/src/lib/FS.js
CHANGED
|
@@ -8,13 +8,10 @@
|
|
|
8
8
|
import {globby} from "globby"
|
|
9
9
|
import path from "node:path"
|
|
10
10
|
import url from "node:url"
|
|
11
|
-
import fs from "node:fs/promises"
|
|
12
|
-
import os from "node:os"
|
|
13
11
|
|
|
14
12
|
import Collection from "../browser/lib/Collection.js"
|
|
15
13
|
import Sass from "./Sass.js"
|
|
16
14
|
import Valid from "./Valid.js"
|
|
17
|
-
import DirectoryObject from "./DirectoryObject.js"
|
|
18
15
|
|
|
19
16
|
/** @typedef {import("./FileObject.js").default} FileObject */
|
|
20
17
|
/** @typedef {import("./DirectoryObject.js").default} DirectoryObject */
|
|
@@ -217,88 +214,4 @@ export default class FS {
|
|
|
217
214
|
// join if no overlap
|
|
218
215
|
return FS.mergeOverlappingPaths(from, normalizedTo)
|
|
219
216
|
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Creates a new temporary directory and wraps it in a DirectoryObject.
|
|
223
|
-
*
|
|
224
|
-
* When called without a parent, creates a new temporary directory in the OS
|
|
225
|
-
* temp folder with a unique name. When called with a parent DirectoryObject,
|
|
226
|
-
* creates a subdirectory within that parent.
|
|
227
|
-
*
|
|
228
|
-
* The parent directory (if provided) must:
|
|
229
|
-
* - Be marked as temporary
|
|
230
|
-
* - Actually exist on the filesystem
|
|
231
|
-
* - Have lineage tracing back to the OS temp directory
|
|
232
|
-
*
|
|
233
|
-
* These validations ensure that only legitimately temporary directories can
|
|
234
|
-
* be created and later removed with the remove() method.
|
|
235
|
-
*
|
|
236
|
-
* @static
|
|
237
|
-
* @async
|
|
238
|
-
* @param {string} name - The base name for the temporary directory. When creating a root temp directory, a random suffix will be appended for uniqueness.
|
|
239
|
-
* @param {DirectoryObject|null} [parent] - Optional parent DirectoryObject to create this directory within. Must be a temporary directory itself.
|
|
240
|
-
* @returns {Promise<DirectoryObject>} A DirectoryObject representing the created temporary directory, with the temporary flag set to true.
|
|
241
|
-
* @throws {Sass} If name is not a string
|
|
242
|
-
* @throws {Sass} If parent is provided but is not a DirectoryObject
|
|
243
|
-
* @throws {Sass} If parent is not marked as temporary
|
|
244
|
-
* @throws {Sass} If parent does not exist
|
|
245
|
-
* @throws {Sass} If parent's lineage does not trace back to the OS temp directory
|
|
246
|
-
* @example
|
|
247
|
-
* // Create a standalone temporary directory
|
|
248
|
-
* const tempDir = await FS.tempDirectory("my-temp")
|
|
249
|
-
* console.log(tempDir.temporary) // true
|
|
250
|
-
* console.log(tempDir.path) // /tmp/my-temp-abc123 (on Unix)
|
|
251
|
-
*
|
|
252
|
-
* @example
|
|
253
|
-
* // Create nested temporary directories
|
|
254
|
-
* const parent = await FS.tempDirectory("parent")
|
|
255
|
-
* const child = await FS.tempDirectory("child", parent)
|
|
256
|
-
* console.log(child.path.startsWith(parent.path)) // true
|
|
257
|
-
* await parent.remove() // Removes both parent and child
|
|
258
|
-
*/
|
|
259
|
-
static async tempDirectory(name, parent=null) {
|
|
260
|
-
Valid.type(name, "String")
|
|
261
|
-
Valid.type(parent, "Null|DirectoryObject")
|
|
262
|
-
|
|
263
|
-
const temp = os.tmpdir()
|
|
264
|
-
|
|
265
|
-
if(parent) {
|
|
266
|
-
Valid.assert(parent.temporary, "Parent must be a temporary DirectoryObject.")
|
|
267
|
-
Valid.assert(await parent.exists, "Parent must exist.")
|
|
268
|
-
|
|
269
|
-
let found = false
|
|
270
|
-
for(const p of parent.walkUp) {
|
|
271
|
-
if(p.path === temp) {
|
|
272
|
-
found = true
|
|
273
|
-
break
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
Valid.assert(found, "The lineage of this directory must be the OS temp directory.")
|
|
278
|
-
|
|
279
|
-
// Security: Reject absolute paths, path traversal, and path separators
|
|
280
|
-
Valid.assert(
|
|
281
|
-
!path.isAbsolute(name),
|
|
282
|
-
"Temporary directory name must not be an absolute path."
|
|
283
|
-
)
|
|
284
|
-
Valid.assert(
|
|
285
|
-
!name.includes("/") && !name.includes("\\") && !name.includes(path.sep),
|
|
286
|
-
"Temporary directory name must not contain path separators."
|
|
287
|
-
)
|
|
288
|
-
Valid.assert(
|
|
289
|
-
name.length > 0,
|
|
290
|
-
"Temporary directory name must not be empty."
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
const dir = new DirectoryObject(path.join(parent.path, name), true)
|
|
294
|
-
await dir.assureExists()
|
|
295
|
-
|
|
296
|
-
return dir
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const prefix = name.endsWith("-") ? name : `${name}-`
|
|
300
|
-
const dir = await fs.mkdtemp(path.join(temp, prefix))
|
|
301
|
-
|
|
302
|
-
return new DirectoryObject(dir, true)
|
|
303
|
-
}
|
|
304
217
|
}
|
package/src/lib/FileObject.js
CHANGED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file TempDirectoryObject.js
|
|
3
|
+
* @description Class representing a temporary directory that is constrained
|
|
4
|
+
* to the OS's temporary directory tree.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import fs from "node:fs"
|
|
8
|
+
import os from "node:os"
|
|
9
|
+
|
|
10
|
+
import CappedDirectoryObject from "./CappedDirectoryObject.js"
|
|
11
|
+
import Sass from "./Sass.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* TempDirectoryObject extends CappedDirectoryObject with the cap set to
|
|
15
|
+
* the OS's temporary directory. Temporary directories are created
|
|
16
|
+
* synchronously during construction and exist immediately.
|
|
17
|
+
*
|
|
18
|
+
* All path operations are validated to ensure they remain within the temp
|
|
19
|
+
* directory hierarchy for security.
|
|
20
|
+
*
|
|
21
|
+
* @augments CappedDirectoryObject
|
|
22
|
+
*/
|
|
23
|
+
export default class TempDirectoryObject extends CappedDirectoryObject {
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Constructs a TempDirectoryObject instance and creates the directory.
|
|
27
|
+
*
|
|
28
|
+
* The directory is created synchronously during construction, so it will
|
|
29
|
+
* exist immediately after the constructor returns.
|
|
30
|
+
*
|
|
31
|
+
* If no name is provided, uses the OS temp directory directly. If a name
|
|
32
|
+
* is provided without a parent, creates a new directory with a unique suffix.
|
|
33
|
+
* If a parent is provided, creates a subdirectory within that parent.
|
|
34
|
+
*
|
|
35
|
+
* @param {string?} [name] - Base name for the temp directory (if empty/null, uses OS temp dir)
|
|
36
|
+
* @param {TempDirectoryObject?} [parent] - Optional parent temporary directory
|
|
37
|
+
* @throws {Sass} If name is absolute
|
|
38
|
+
* @throws {Sass} If name is empty (when parent is provided)
|
|
39
|
+
* @throws {Sass} If name contains path separators
|
|
40
|
+
* @throws {Sass} If parent is provided but not a temporary directory
|
|
41
|
+
* @throws {Sass} If parent's lineage does not trace back to the OS temp directory
|
|
42
|
+
* @throws {Sass} If directory creation fails
|
|
43
|
+
* @example
|
|
44
|
+
* // Use OS temp directory directly
|
|
45
|
+
* const temp = new TempDirectoryObject()
|
|
46
|
+
* console.log(temp.path) // "/tmp"
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* // Create with unique name
|
|
50
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
51
|
+
* console.log(temp.path) // "/tmp/myapp-ABC123"
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Nested temp directories
|
|
55
|
+
* const parent = new TempDirectoryObject("parent")
|
|
56
|
+
* const child = new TempDirectoryObject("child", parent)
|
|
57
|
+
* await parent.remove() // Removes both parent and child
|
|
58
|
+
*/
|
|
59
|
+
constructor(name, parent=null) {
|
|
60
|
+
let finalName = name
|
|
61
|
+
|
|
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}`
|
|
71
|
+
}
|
|
72
|
+
|
|
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)
|
|
76
|
+
|
|
77
|
+
// Temp-specific behavior: create directory immediately
|
|
78
|
+
this.#createDirectory()
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Creates the directory synchronously on the filesystem.
|
|
83
|
+
*
|
|
84
|
+
* @private
|
|
85
|
+
* @throws {Sass} If directory creation fails
|
|
86
|
+
*/
|
|
87
|
+
#createDirectory() {
|
|
88
|
+
try {
|
|
89
|
+
fs.mkdirSync(this.path)
|
|
90
|
+
} catch(e) {
|
|
91
|
+
// EEXIST is fine - directory already exists
|
|
92
|
+
if(e.code !== "EEXIST") {
|
|
93
|
+
throw Sass.new(
|
|
94
|
+
`Unable to create temporary directory '${this.path}': ${e.message}`
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Creates a new TempDirectoryObject by extending this directory's path.
|
|
102
|
+
*
|
|
103
|
+
* Validates that the resulting path remains within the temp directory tree.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} newPath - The path segment to append
|
|
106
|
+
* @returns {TempDirectoryObject} A new TempDirectoryObject with the extended path
|
|
107
|
+
* @throws {Sass} If the path would escape the temp directory
|
|
108
|
+
* @throws {Sass} If the path is absolute
|
|
109
|
+
* @throws {Sass} If the path contains traversal (..)
|
|
110
|
+
* @example
|
|
111
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
112
|
+
* const subDir = temp.addDirectory("data")
|
|
113
|
+
* console.log(subDir.path) // "/tmp/myapp-ABC123/data"
|
|
114
|
+
*/
|
|
115
|
+
addDirectory(newPath) {
|
|
116
|
+
// Delegate to base class addDirectory() which will call TempDirectoryObject constructor
|
|
117
|
+
return super.addDirectory(newPath)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Creates a new FileObject by extending this directory's path.
|
|
122
|
+
*
|
|
123
|
+
* Validates that the resulting path remains within the temp directory tree.
|
|
124
|
+
*
|
|
125
|
+
* @param {string} filename - The filename to append
|
|
126
|
+
* @returns {FileObject} A new FileObject with the extended path
|
|
127
|
+
* @throws {Sass} If the path would escape the temp directory
|
|
128
|
+
* @throws {Sass} If the path is absolute
|
|
129
|
+
* @throws {Sass} If the path contains traversal (..)
|
|
130
|
+
* @example
|
|
131
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
132
|
+
* const file = temp.addFile("config.json")
|
|
133
|
+
* console.log(file.path) // "/tmp/myapp-ABC123/config.json"
|
|
134
|
+
*/
|
|
135
|
+
addFile(filename) {
|
|
136
|
+
// Delegate to base class addFile() which handles security checks
|
|
137
|
+
return super.addFile(filename)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns a string representation of the TempDirectoryObject.
|
|
142
|
+
*
|
|
143
|
+
* @returns {string} string representation of the TempDirectoryObject
|
|
144
|
+
*/
|
|
145
|
+
toString() {
|
|
146
|
+
return `[TempDirectoryObject: ${this.path}]`
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/types/index.d.ts
CHANGED
|
@@ -6,8 +6,10 @@ export { default as Sass } from "./lib/Sass.js";
|
|
|
6
6
|
export { default as Tantrum } from "./lib/Tantrum.js";
|
|
7
7
|
export { default as Util } from "./lib/Util.js";
|
|
8
8
|
export { default as Cache } from "./lib/Cache.js";
|
|
9
|
+
export { default as CappedDirectoryObject } from "./lib/CappedDirectoryObject.js";
|
|
9
10
|
export { default as Contract } from "./lib/Contract.js";
|
|
10
11
|
export { default as DirectoryObject } from "./lib/DirectoryObject.js";
|
|
12
|
+
export { default as TempDirectoryObject } from "./lib/TempDirectoryObject.js";
|
|
11
13
|
export { default as FileObject } from "./lib/FileObject.js";
|
|
12
14
|
export { default as FS } from "./lib/FS.js";
|
|
13
15
|
export { default as Glog } from "./lib/Glog.js";
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CappedDirectoryObject extends DirectoryObject with constraints that ensure
|
|
3
|
+
* all operations are restricted to a specific directory tree (the "cap").
|
|
4
|
+
*
|
|
5
|
+
* All path operations are validated to ensure they remain within the
|
|
6
|
+
* cap directory hierarchy for security.
|
|
7
|
+
*
|
|
8
|
+
* @augments DirectoryObject
|
|
9
|
+
*/
|
|
10
|
+
export default class CappedDirectoryObject extends DirectoryObject {
|
|
11
|
+
/**
|
|
12
|
+
* Constructs a CappedDirectoryObject instance.
|
|
13
|
+
*
|
|
14
|
+
* This is an abstract base class - use subclasses like TempDirectoryObject
|
|
15
|
+
* that define specific caps.
|
|
16
|
+
*
|
|
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
|
|
19
|
+
* @param {CappedDirectoryObject?} [parent] - Optional parent capped directory
|
|
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
|
|
26
|
+
* @throws {Sass} If the resulting path would escape the cap
|
|
27
|
+
*/
|
|
28
|
+
constructor(name: string | null, cap: string, parent?: CappedDirectoryObject | null, temporary?: boolean);
|
|
29
|
+
/**
|
|
30
|
+
* Returns the cap path for this directory.
|
|
31
|
+
*
|
|
32
|
+
* @returns {string} The cap directory path
|
|
33
|
+
*/
|
|
34
|
+
get cap(): string;
|
|
35
|
+
/**
|
|
36
|
+
* Returns whether this directory is capped.
|
|
37
|
+
*
|
|
38
|
+
* @returns {boolean} Always true for CappedDirectoryObject instances
|
|
39
|
+
*/
|
|
40
|
+
get capped(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Creates a new CappedDirectoryObject by extending this directory's path.
|
|
43
|
+
*
|
|
44
|
+
* Validates that the resulting path remains within the cap directory tree.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} newPath - The path segment to append
|
|
47
|
+
* @returns {CappedDirectoryObject} A new CappedDirectoryObject with the extended path
|
|
48
|
+
* @throws {Sass} If the path would escape the cap directory
|
|
49
|
+
* @throws {Sass} If the path is absolute
|
|
50
|
+
* @throws {Sass} If the path contains traversal (..)
|
|
51
|
+
* @example
|
|
52
|
+
* const capped = new TempDirectoryObject("myapp")
|
|
53
|
+
* const subDir = capped.addDirectory("data")
|
|
54
|
+
* console.log(subDir.path) // "/tmp/myapp-ABC123/data"
|
|
55
|
+
*/
|
|
56
|
+
addDirectory(newPath: string): CappedDirectoryObject;
|
|
57
|
+
#private;
|
|
58
|
+
}
|
|
59
|
+
import DirectoryObject from "./DirectoryObject.js";
|
|
60
|
+
//# sourceMappingURL=CappedDirectoryObject.d.ts.map
|
|
@@ -0,0 +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;IAED;;;;;;;;;;;;;;OAcG;IACH,sBAVW,MAAM,GACJ,qBAAqB,CA0BjC;;CA6CF;4BA/N2B,sBAAsB"}
|
|
@@ -102,7 +102,8 @@ export default class DirectoryObject extends FS {
|
|
|
102
102
|
* @throws {Sass} If the directory is not marked as temporary
|
|
103
103
|
* @throws {Sass} If the directory deletion fails
|
|
104
104
|
* @example
|
|
105
|
-
* const tempDir =
|
|
105
|
+
* const tempDir = new TempDirectoryObject("my-temp")
|
|
106
|
+
* await tempDir.assureExists()
|
|
106
107
|
* // ... use the directory ...
|
|
107
108
|
* await tempDir.remove() // Recursively deletes everything
|
|
108
109
|
*/
|
|
@@ -190,20 +191,32 @@ export default class DirectoryObject extends FS {
|
|
|
190
191
|
*/
|
|
191
192
|
hasDirectory(dirname: string): Promise<boolean>;
|
|
192
193
|
/**
|
|
193
|
-
* Creates a new DirectoryObject by
|
|
194
|
-
* path.
|
|
194
|
+
* Creates a new DirectoryObject by extending this directory's path.
|
|
195
195
|
*
|
|
196
196
|
* Uses overlapping path segment detection to intelligently combine paths.
|
|
197
197
|
* Preserves the temporary flag from the current directory.
|
|
198
198
|
*
|
|
199
|
-
* @param {string} newPath - The path to
|
|
200
|
-
* @returns {DirectoryObject} A new DirectoryObject with the
|
|
199
|
+
* @param {string} newPath - The path to append to this directory's path.
|
|
200
|
+
* @returns {DirectoryObject} A new DirectoryObject with the extended path.
|
|
201
201
|
* @example
|
|
202
202
|
* const dir = new DirectoryObject("/projects/git/toolkit")
|
|
203
|
-
* const subDir = dir.
|
|
203
|
+
* const subDir = dir.addDirectory("src/lib")
|
|
204
204
|
* console.log(subDir.path) // "/projects/git/toolkit/src/lib"
|
|
205
205
|
*/
|
|
206
|
-
|
|
206
|
+
addDirectory(newPath: string): DirectoryObject;
|
|
207
|
+
/**
|
|
208
|
+
* Creates a new FileObject by extending this directory's path.
|
|
209
|
+
*
|
|
210
|
+
* Uses overlapping path segment detection to intelligently combine paths.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} filename - The filename to append to this directory's path.
|
|
213
|
+
* @returns {FileObject} A new FileObject with the extended path.
|
|
214
|
+
* @example
|
|
215
|
+
* const dir = new DirectoryObject("/projects/git/toolkit")
|
|
216
|
+
* const file = dir.addFile("package.json")
|
|
217
|
+
* console.log(file.path) // "/projects/git/toolkit/package.json"
|
|
218
|
+
*/
|
|
219
|
+
addFile(filename: string): FileObject;
|
|
207
220
|
#private;
|
|
208
221
|
}
|
|
209
222
|
import FS from "./FS.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/DirectoryObject.js"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;GAaG;AACH;uBA4Fe,MAAM;IAjEnB;;;;;OAKG;IACH,yCAFW,OAAO,EA6BjB;IAWD;;;;OAIG;IACH,UAFa,MAAM,CAalB;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;IAED;;;;OAIG;IACH,WAFa,MAAM,CAIlB;IAED;;;;;;;OAOG;IACH,aALa,KAAK,CAAC,MAAM,CAAC,CAOzB;IAED;;;;OAIG;IACH,iBAFa,OAAO,CAInB;IAED
|
|
1
|
+
{"version":3,"file":"DirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/DirectoryObject.js"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;GAaG;AACH;uBA4Fe,MAAM;IAjEnB;;;;;OAKG;IACH,yCAFW,OAAO,EA6BjB;IAWD;;;;OAIG;IACH,UAFa,MAAM,CAalB;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;IAED;;;;OAIG;IACH,WAFa,MAAM,CAIlB;IAED;;;;;;;OAOG;IACH,aALa,KAAK,CAAC,MAAM,CAAC,CAOzB;IAED;;;;OAIG;IACH,iBAFa,OAAO,CAInB;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,UATa,OAAO,CAAC,IAAI,CAAC,CA0BzB;IAED;;;;OAIG;IACH,cAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,mBAFa,OAAO,CAInB;IAiBD;;;;OAIG;IACH,QAFa,OAAO,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAAC,WAAW,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;KAAC,CAAC,CAkBpF;IAED;;;;;;;;;;;;OAYG;IACH,uBARW,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAqBzB;IA8BD;;;;;;;;;;;;;;;OAeG;IACH,cAZa,MAAM,CAclB;IAED;;;;;;;;;;;;;;OAcG;IACH,UARa,OAAO,CAAC,IAAI,CAAC,CAkBzB;IAED;;;;;OAKG;IACH,kBAHW,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAM5B;IAED;;;;;OAKG;IACH,sBAHW,MAAM,GACJ,OAAO,CAAC,OAAO,CAAC,CAO5B;IAED;;;;;;;;;;;;OAYG;IACH,sBAPW,MAAM,GACJ,eAAe,CAa3B;IAED;;;;;;;;;;;OAWG;IACH,kBAPW,MAAM,GACJ,UAAU,CAatB;;CACF;eAxdc,SAAS;uBACD,iBAAiB"}
|
package/src/types/lib/FS.d.ts
CHANGED
|
@@ -71,47 +71,7 @@ export default class FS {
|
|
|
71
71
|
* @returns {string} The resolved path
|
|
72
72
|
*/
|
|
73
73
|
static resolvePath(fromPath: string, toPath: string): string;
|
|
74
|
-
/**
|
|
75
|
-
* Creates a new temporary directory and wraps it in a DirectoryObject.
|
|
76
|
-
*
|
|
77
|
-
* When called without a parent, creates a new temporary directory in the OS
|
|
78
|
-
* temp folder with a unique name. When called with a parent DirectoryObject,
|
|
79
|
-
* creates a subdirectory within that parent.
|
|
80
|
-
*
|
|
81
|
-
* The parent directory (if provided) must:
|
|
82
|
-
* - Be marked as temporary
|
|
83
|
-
* - Actually exist on the filesystem
|
|
84
|
-
* - Have lineage tracing back to the OS temp directory
|
|
85
|
-
*
|
|
86
|
-
* These validations ensure that only legitimately temporary directories can
|
|
87
|
-
* be created and later removed with the remove() method.
|
|
88
|
-
*
|
|
89
|
-
* @static
|
|
90
|
-
* @async
|
|
91
|
-
* @param {string} name - The base name for the temporary directory. When creating a root temp directory, a random suffix will be appended for uniqueness.
|
|
92
|
-
* @param {DirectoryObject|null} [parent] - Optional parent DirectoryObject to create this directory within. Must be a temporary directory itself.
|
|
93
|
-
* @returns {Promise<DirectoryObject>} A DirectoryObject representing the created temporary directory, with the temporary flag set to true.
|
|
94
|
-
* @throws {Sass} If name is not a string
|
|
95
|
-
* @throws {Sass} If parent is provided but is not a DirectoryObject
|
|
96
|
-
* @throws {Sass} If parent is not marked as temporary
|
|
97
|
-
* @throws {Sass} If parent does not exist
|
|
98
|
-
* @throws {Sass} If parent's lineage does not trace back to the OS temp directory
|
|
99
|
-
* @example
|
|
100
|
-
* // Create a standalone temporary directory
|
|
101
|
-
* const tempDir = await FS.tempDirectory("my-temp")
|
|
102
|
-
* console.log(tempDir.temporary) // true
|
|
103
|
-
* console.log(tempDir.path) // /tmp/my-temp-abc123 (on Unix)
|
|
104
|
-
*
|
|
105
|
-
* @example
|
|
106
|
-
* // Create nested temporary directories
|
|
107
|
-
* const parent = await FS.tempDirectory("parent")
|
|
108
|
-
* const child = await FS.tempDirectory("child", parent)
|
|
109
|
-
* console.log(child.path.startsWith(parent.path)) // true
|
|
110
|
-
* await parent.remove() // Removes both parent and child
|
|
111
|
-
*/
|
|
112
|
-
static tempDirectory(name: string, parent?: DirectoryObject | null): Promise<DirectoryObject>;
|
|
113
74
|
}
|
|
114
75
|
export type FileObject = import("./FileObject.js").default;
|
|
115
76
|
export type DirectoryObject = import("./DirectoryObject.js").default;
|
|
116
|
-
import DirectoryObject from "./DirectoryObject.js";
|
|
117
77
|
//# sourceMappingURL=FS.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FS.d.ts","sourceRoot":"","sources":["../../lib/FS.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"FS.d.ts","sourceRoot":"","sources":["../../lib/FS.js"],"names":[],"mappings":"AAwBA;;GAEG;AACH;IACE,kCAAwB;IACxB,uCAAkC;IAClC,mBAAsB;IAEtB;;;;;;OAMG;IACH,4BAHW,MAAM,GACJ,MAAM,CAIlB;IAED;;;;;;OAMG;IACH,2BAHW,MAAM,GACJ,MAAM,CAUlB;IAED;;;;;;OAMG;IACH,2BAHW,MAAM,GACJ,MAAM,CAQlB;IAED;;;;;;;;OAQG;IACH,sBALW,MAAM,GAAC,KAAK,CAAC,MAAM,CAAC,GAClB,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAmCtC;IAED;;;;;;;;;;OAUG;IACH,oCAJW,UAAU,GAAC,eAAe,MAC1B,UAAU,GAAC,eAAe,GACxB,MAAM,CAYlB;IAED;;;;;;;;OAQG;IACH,oCALW,MAAM,SACN,MAAM,QACN,MAAM,GACJ,MAAM,CA2BlB;IAED;;;;;;;;OAQG;IACH,6BAJW,MAAM,UACN,MAAM,GACJ,MAAM,CAgClB;CACF;yBAzMa,OAAO,iBAAiB,EAAE,OAAO;8BACjC,OAAO,sBAAsB,EAAE,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileObject.d.ts","sourceRoot":"","sources":["../../lib/FileObject.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;GAcG;AAEH;
|
|
1
|
+
{"version":3,"file":"FileObject.d.ts","sourceRoot":"","sources":["../../lib/FileObject.js"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;GAcG;AAEH;uBAuHe,MAAM;IAtHnB;;;;;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,cACnB,eAAe,GAAC,MAAM,GAAC,IAAI,EA2CrC;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,iBAFa,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;eAtgBc,SAAS;4BADI,sBAAsB;kBARhC,OAAO;iBAIR,MAAM"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TempDirectoryObject extends CappedDirectoryObject with the cap set to
|
|
3
|
+
* the OS's temporary directory. Temporary directories are created
|
|
4
|
+
* synchronously during construction and exist immediately.
|
|
5
|
+
*
|
|
6
|
+
* All path operations are validated to ensure they remain within the temp
|
|
7
|
+
* directory hierarchy for security.
|
|
8
|
+
*
|
|
9
|
+
* @augments CappedDirectoryObject
|
|
10
|
+
*/
|
|
11
|
+
export default class TempDirectoryObject extends CappedDirectoryObject {
|
|
12
|
+
/**
|
|
13
|
+
* Constructs a TempDirectoryObject instance and creates the directory.
|
|
14
|
+
*
|
|
15
|
+
* The directory is created synchronously during construction, so it will
|
|
16
|
+
* exist immediately after the constructor returns.
|
|
17
|
+
*
|
|
18
|
+
* If no name is provided, uses the OS temp directory directly. If a name
|
|
19
|
+
* is provided without a parent, creates a new directory with a unique suffix.
|
|
20
|
+
* If a parent is provided, creates a subdirectory within that parent.
|
|
21
|
+
*
|
|
22
|
+
* @param {string?} [name] - Base name for the temp directory (if empty/null, uses OS temp dir)
|
|
23
|
+
* @param {TempDirectoryObject?} [parent] - Optional parent temporary directory
|
|
24
|
+
* @throws {Sass} If name is absolute
|
|
25
|
+
* @throws {Sass} If name is empty (when parent is provided)
|
|
26
|
+
* @throws {Sass} If name contains path separators
|
|
27
|
+
* @throws {Sass} If parent is provided but not a temporary directory
|
|
28
|
+
* @throws {Sass} If parent's lineage does not trace back to the OS temp directory
|
|
29
|
+
* @throws {Sass} If directory creation fails
|
|
30
|
+
* @example
|
|
31
|
+
* // Use OS temp directory directly
|
|
32
|
+
* const temp = new TempDirectoryObject()
|
|
33
|
+
* console.log(temp.path) // "/tmp"
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* // Create with unique name
|
|
37
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
38
|
+
* console.log(temp.path) // "/tmp/myapp-ABC123"
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* // Nested temp directories
|
|
42
|
+
* const parent = new TempDirectoryObject("parent")
|
|
43
|
+
* const child = new TempDirectoryObject("child", parent)
|
|
44
|
+
* await parent.remove() // Removes both parent and child
|
|
45
|
+
*/
|
|
46
|
+
constructor(name?: string | null, parent?: TempDirectoryObject | null);
|
|
47
|
+
/**
|
|
48
|
+
* Creates a new TempDirectoryObject by extending this directory's path.
|
|
49
|
+
*
|
|
50
|
+
* Validates that the resulting path remains within the temp directory tree.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} newPath - The path segment to append
|
|
53
|
+
* @returns {TempDirectoryObject} A new TempDirectoryObject with the extended path
|
|
54
|
+
* @throws {Sass} If the path would escape the temp directory
|
|
55
|
+
* @throws {Sass} If the path is absolute
|
|
56
|
+
* @throws {Sass} If the path contains traversal (..)
|
|
57
|
+
* @example
|
|
58
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
59
|
+
* const subDir = temp.addDirectory("data")
|
|
60
|
+
* console.log(subDir.path) // "/tmp/myapp-ABC123/data"
|
|
61
|
+
*/
|
|
62
|
+
addDirectory(newPath: string): TempDirectoryObject;
|
|
63
|
+
/**
|
|
64
|
+
* Creates a new FileObject by extending this directory's path.
|
|
65
|
+
*
|
|
66
|
+
* Validates that the resulting path remains within the temp directory tree.
|
|
67
|
+
*
|
|
68
|
+
* @param {string} filename - The filename to append
|
|
69
|
+
* @returns {FileObject} A new FileObject with the extended path
|
|
70
|
+
* @throws {Sass} If the path would escape the temp directory
|
|
71
|
+
* @throws {Sass} If the path is absolute
|
|
72
|
+
* @throws {Sass} If the path contains traversal (..)
|
|
73
|
+
* @example
|
|
74
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
75
|
+
* const file = temp.addFile("config.json")
|
|
76
|
+
* console.log(file.path) // "/tmp/myapp-ABC123/config.json"
|
|
77
|
+
*/
|
|
78
|
+
addFile(filename: string): FileObject;
|
|
79
|
+
#private;
|
|
80
|
+
}
|
|
81
|
+
import CappedDirectoryObject from "./CappedDirectoryObject.js";
|
|
82
|
+
//# sourceMappingURL=TempDirectoryObject.d.ts.map
|
|
@@ -0,0 +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"}
|