@gesslar/toolkit 2.7.1 → 2.9.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/README.md +9 -0
- package/package.json +1 -1
- package/src/browser/index.js +1 -0
- package/src/browser/lib/Promised.js +120 -0
- package/src/browser/lib/Util.js +0 -98
- package/src/index.js +1 -0
- package/src/lib/CappedDirectoryObject.js +341 -42
- package/src/lib/FileObject.js +52 -13
- package/src/lib/TempDirectoryObject.js +2 -2
- package/src/types/browser/index.d.ts +1 -0
- package/src/types/browser/lib/Promised.d.ts +119 -0
- package/src/types/browser/lib/Promised.d.ts.map +1 -0
- package/src/types/browser/lib/Util.d.ts +0 -67
- package/src/types/browser/lib/Util.d.ts.map +1 -1
- package/src/types/index.d.ts +1 -0
- package/src/types/lib/CappedDirectoryObject.d.ts +53 -6
- package/src/types/lib/CappedDirectoryObject.d.ts.map +1 -1
- package/src/types/lib/FileObject.d.ts +21 -2
- package/src/types/lib/FileObject.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ a Tauri app.
|
|
|
16
16
|
| Disposer | Lifecycle management for disposable resources |
|
|
17
17
|
| HTML | HTML loading and sanitization utilities |
|
|
18
18
|
| Notify | Event system wrapper for DOM events |
|
|
19
|
+
| Promised | Promise utilities for settling, filtering, and extracting values from promise results |
|
|
19
20
|
| Sass | Custom Error class with enhanced features |
|
|
20
21
|
| Tantrum | AggregateError implementation |
|
|
21
22
|
| Type | String-based type management (exported as TypeSpec in browser) |
|
|
@@ -93,3 +94,11 @@ import { Data, Collection, Util } from '@gesslar/toolkit/browser'
|
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
The browser version includes: Collection, Data, Disposer, HTML, Notify, Sass, Tantrum, Type (TypeSpec), Util, and Valid. Node-only modules (Cache, CappedDirectoryObject, Contract, DirectoryObject, FileObject, FS, Glog, Schemer, TempDirectoryObject, Term, Terms) are not available in the browser version.
|
|
97
|
+
|
|
98
|
+
## Post Partum
|
|
99
|
+
|
|
100
|
+
If you made it this far, please understand that I have absolutely zero scruples when it comes to breaking changes. Primarily, the audience for this library is myself. Consequently, anything that relies on the contents of this library will dutifully crash and I'll have to refactor those things then. It's like leaving playing nicky nicky nine doors. But with myself. And there's a lazy bomb waiting for me. That I planted. For me. And the bomb just explodes poop.
|
|
101
|
+
|
|
102
|
+
You're of course welcome to use my library! It's pretty robust. Uhhh, but maybe lock in the version until you see if something is gonna poop all over you. I make robots make my PR notifications and generally they're very good at firing off klaxons about my fetish for breaking changes, so you should be all right if you're paying attention. 🤷🏻
|
|
103
|
+
|
|
104
|
+
Sincerely, Senator Yabba of the Dabba (Doo)
|
package/package.json
CHANGED
package/src/browser/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export {Disposer as DisposerClass} from "./lib/Disposer.js"
|
|
|
8
8
|
export {default as HTML} from "./lib/HTML.js"
|
|
9
9
|
export {HTML as HTMLClass} from "./lib/HTML.js"
|
|
10
10
|
export {default as Notify} from "./lib/Notify.js"
|
|
11
|
+
export {default as Promised} from "./lib/Promised.js"
|
|
11
12
|
export {default as Sass} from "./lib/Sass.js"
|
|
12
13
|
export {default as Tantrum} from "./lib/Tantrum.js"
|
|
13
14
|
export {default as Type} from "./lib/TypeSpec.js"
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import Tantrum from "./Tantrum.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Utility class providing helper functions for working with Promises,
|
|
5
|
+
* including settling, filtering, and extracting values from promise results.
|
|
6
|
+
*/
|
|
7
|
+
export default class Promised {
|
|
8
|
+
/**
|
|
9
|
+
* Asynchronously awaits all promises in parallel.
|
|
10
|
+
* Wrapper around Promise.all for consistency with other utility methods.
|
|
11
|
+
*
|
|
12
|
+
* @param {Array<Promise<unknown>>} promises - Array of promises to await
|
|
13
|
+
* @returns {Promise<Array<unknown>>} Results of all promises
|
|
14
|
+
*/
|
|
15
|
+
static async await(promises) {
|
|
16
|
+
return await Promise.all(promises)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Settles all promises (both fulfilled and rejected) in parallel.
|
|
21
|
+
* Wrapper around Promise.allSettled for consistency with other utility methods.
|
|
22
|
+
*
|
|
23
|
+
* @param {Array<Promise<unknown>>} promises - Array of promises to settle
|
|
24
|
+
* @returns {Promise<Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>>} Results of all settled promises with status and value/reason
|
|
25
|
+
*/
|
|
26
|
+
static async settle(promises) {
|
|
27
|
+
return await Promise.allSettled(promises)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Checks if any result in the settled promise array is rejected.
|
|
32
|
+
*
|
|
33
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
34
|
+
* @returns {boolean} True if any result is rejected, false otherwise
|
|
35
|
+
*/
|
|
36
|
+
static hasRejected(settled) {
|
|
37
|
+
return settled.some(r => r.status === "rejected")
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Checks if any result in the settled promise array is fulfilled.
|
|
42
|
+
*
|
|
43
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
44
|
+
* @returns {boolean} True if any result is fulfilled, false otherwise
|
|
45
|
+
*/
|
|
46
|
+
static hasFulfilled(settled) {
|
|
47
|
+
return settled.some(r => r.status === "fulfilled")
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Filters and returns all rejected results from a settled promise array.
|
|
52
|
+
*
|
|
53
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
54
|
+
* @returns {Array<{status: 'rejected', reason: unknown}>} Array of rejected results
|
|
55
|
+
*/
|
|
56
|
+
static rejected(settled) {
|
|
57
|
+
return settled.filter(r => r.status === "rejected")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Filters and returns all fulfilled results from a settled promise array.
|
|
62
|
+
*
|
|
63
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} result - Array of settled promise results
|
|
64
|
+
* @returns {Array<{status: 'fulfilled', value: unknown}>} Array of fulfilled results
|
|
65
|
+
*/
|
|
66
|
+
static fulfilled(result) {
|
|
67
|
+
return result.filter(r => r.status === "fulfilled")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Extracts the rejection reasons from a settled promise array.
|
|
72
|
+
*
|
|
73
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
74
|
+
* @returns {Array<unknown>} Array of rejection reasons
|
|
75
|
+
*/
|
|
76
|
+
static reasons(settled) {
|
|
77
|
+
const rejected = this.rejected(settled)
|
|
78
|
+
const reasons = rejected.map(e => e.reason)
|
|
79
|
+
|
|
80
|
+
return reasons
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Extracts the values from fulfilled results in a settled promise array.
|
|
85
|
+
*
|
|
86
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
87
|
+
* @returns {Array<unknown>} Array of fulfilled values
|
|
88
|
+
*/
|
|
89
|
+
static values(settled) {
|
|
90
|
+
const fulfilled = this.fulfilled(settled)
|
|
91
|
+
const values = fulfilled.map(e => e.value)
|
|
92
|
+
|
|
93
|
+
return values
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Throws a Tantrum containing all rejection reasons from settled promises.
|
|
98
|
+
*
|
|
99
|
+
* @param {string} message - Error message. Defaults to "GIGO"
|
|
100
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
101
|
+
* @throws {Tantrum} Throws a Tantrum error with rejection reasons
|
|
102
|
+
*/
|
|
103
|
+
static throw(message="GIGO", settled) {
|
|
104
|
+
const rejected = this.rejected(settled)
|
|
105
|
+
const reasons = this.reasons(rejected)
|
|
106
|
+
|
|
107
|
+
throw Tantrum.new(message, reasons)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Returns the first promise to resolve or reject from an array of promises.
|
|
112
|
+
* Wrapper around Promise.race for consistency with other utility methods.
|
|
113
|
+
*
|
|
114
|
+
* @param {Array<Promise<unknown>>} promises - Array of promises to race
|
|
115
|
+
* @returns {Promise<unknown>} Result of the first settled promise
|
|
116
|
+
*/
|
|
117
|
+
static async race(promises) {
|
|
118
|
+
return await Promise.race(promises)
|
|
119
|
+
}
|
|
120
|
+
}
|
package/src/browser/lib/Util.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import Tantrum from "./Tantrum.js"
|
|
2
1
|
import Valid from "./Valid.js"
|
|
3
2
|
import Collection from "./Collection.js"
|
|
4
3
|
|
|
@@ -80,103 +79,6 @@ export default class Util {
|
|
|
80
79
|
return `${" ".repeat(leftPadding)}${work}${" ".repeat(rightPadding)}`
|
|
81
80
|
}
|
|
82
81
|
|
|
83
|
-
/**
|
|
84
|
-
* Asynchronously awaits all promises in parallel.
|
|
85
|
-
* Wrapper around Promise.all for consistency with other utility methods.
|
|
86
|
-
*
|
|
87
|
-
* @param {Array<Promise<unknown>>} promises - Array of promises to await
|
|
88
|
-
* @returns {Promise<Array<unknown>>} Results of all promises
|
|
89
|
-
*/
|
|
90
|
-
static async awaitAll(promises) {
|
|
91
|
-
return await Promise.all(promises)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Settles all promises (both fulfilled and rejected) in parallel.
|
|
96
|
-
* Wrapper around Promise.allSettled for consistency with other utility methods.
|
|
97
|
-
*
|
|
98
|
-
* @param {Array<Promise<unknown>>} promises - Array of promises to settle
|
|
99
|
-
* @returns {Promise<Array<object>>} Results of all settled promises with status and value/reason
|
|
100
|
-
*/
|
|
101
|
-
static async settleAll(promises) {
|
|
102
|
-
return await Promise.allSettled(promises)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Checks if any result in the settled promise array is rejected.
|
|
107
|
-
*
|
|
108
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
109
|
-
* @returns {boolean} True if any result is rejected, false otherwise.
|
|
110
|
-
*/
|
|
111
|
-
static anyRejected(result) {
|
|
112
|
-
return result.some(r => r.status === "rejected")
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Filters and returns all rejected results from a settled promise array.
|
|
117
|
-
*
|
|
118
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
119
|
-
* @returns {Array<object>} Array of rejected results.
|
|
120
|
-
*/
|
|
121
|
-
static settledAndRejected(result) {
|
|
122
|
-
return result.filter(r => r.status === "rejected")
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Extracts the rejection reasons from an array of rejected promise results.
|
|
127
|
-
*
|
|
128
|
-
* @param {Array<object>} rejected - Array of rejected results.
|
|
129
|
-
* @returns {Array<unknown>} Array of rejection reasons.
|
|
130
|
-
*/
|
|
131
|
-
static rejectedReasons(rejected) {
|
|
132
|
-
return rejected.map(r => r.reason)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Throws a Sass error containing all rejection reasons from settled promises.
|
|
137
|
-
*
|
|
138
|
-
* @param {string} [_message] - Optional error message. Defaults to "GIGO"
|
|
139
|
-
* @param {Array<object>} rejected - Array of rejected results.
|
|
140
|
-
* @throws {Error} Throws a Tantrum error with rejection reasons.
|
|
141
|
-
*/
|
|
142
|
-
static throwRejected(message="GIGO", settled) {
|
|
143
|
-
throw Tantrum.new(
|
|
144
|
-
message,
|
|
145
|
-
this.rejectedReasons(this.settledAndRejected(settled))
|
|
146
|
-
)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Filters and returns all fulfilled results from a settled promise array.
|
|
151
|
-
*
|
|
152
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
153
|
-
* @returns {Array<object>} Array of fulfilled results.
|
|
154
|
-
*/
|
|
155
|
-
static settledAndFulfilled(result) {
|
|
156
|
-
return result.filter(r => r.status === "fulfilled")
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Extracts the values from all fulfilled results in a settled promise array.
|
|
161
|
-
*
|
|
162
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
163
|
-
* @returns {Array<unknown>} Array of fulfilled values.
|
|
164
|
-
*/
|
|
165
|
-
static fulfilledValues(result) {
|
|
166
|
-
return this.settledAndFulfilled(result).map(r => r.value)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Returns the first promise to resolve or reject from an array of promises.
|
|
171
|
-
* Wrapper around Promise.race for consistency with other utility methods.
|
|
172
|
-
*
|
|
173
|
-
* @param {Array<Promise<unknown>>} promises - Array of promises to race
|
|
174
|
-
* @returns {Promise<unknown>} Result of the first settled promise
|
|
175
|
-
*/
|
|
176
|
-
static async race(promises) {
|
|
177
|
-
return await Promise.race(promises)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
82
|
/**
|
|
181
83
|
* Determine the Levenshtein distance between two string values
|
|
182
84
|
*
|
package/src/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export {default as Collection} from "./browser/lib/Collection.js"
|
|
|
3
3
|
export {default as Data} from "./browser/lib/Data.js"
|
|
4
4
|
export {default as Disposer} from "./browser/lib/Disposer.js"
|
|
5
5
|
export {Disposer as DisposerClass} from "./browser/lib/Disposer.js"
|
|
6
|
+
export {default as Promised} from "./browser/lib/Promised.js"
|
|
6
7
|
export {default as Type} from "./browser/lib/TypeSpec.js"
|
|
7
8
|
export {default as Valid} from "./lib/Valid.js"
|
|
8
9
|
|
|
@@ -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
|
/**
|
|
@@ -84,7 +85,8 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
84
85
|
"Parent must have the same cap as this directory.",
|
|
85
86
|
)
|
|
86
87
|
|
|
87
|
-
|
|
88
|
+
// Use real path for filesystem operations
|
|
89
|
+
const parentPath = parent.realPath || parent.path
|
|
88
90
|
|
|
89
91
|
// Validate parent's lineage traces back to the cap
|
|
90
92
|
let found = false
|
|
@@ -128,13 +130,13 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
128
130
|
*/
|
|
129
131
|
#validateCapPath() {
|
|
130
132
|
const cap = this.#cap
|
|
131
|
-
const resolved = path.resolve(this
|
|
133
|
+
const resolved = path.resolve(this.#realPath)
|
|
132
134
|
const capResolved = path.resolve(cap)
|
|
133
135
|
|
|
134
136
|
// Check if the resolved path starts with the cap directory
|
|
135
137
|
if(!resolved.startsWith(capResolved)) {
|
|
136
138
|
throw Sass.new(
|
|
137
|
-
`Path '${this
|
|
139
|
+
`Path '${this.#realPath}' is not within the cap directory '${cap}'`
|
|
138
140
|
)
|
|
139
141
|
}
|
|
140
142
|
}
|
|
@@ -157,6 +159,73 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
157
159
|
return true
|
|
158
160
|
}
|
|
159
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Returns the real filesystem path (for internal and subclass use).
|
|
164
|
+
*
|
|
165
|
+
* @protected
|
|
166
|
+
* @returns {string} The actual filesystem path
|
|
167
|
+
*/
|
|
168
|
+
get realPath() {
|
|
169
|
+
return super.path
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Private alias for realPath (for use in private methods).
|
|
174
|
+
*
|
|
175
|
+
* @private
|
|
176
|
+
* @returns {string} The actual filesystem path
|
|
177
|
+
*/
|
|
178
|
+
get #realPath() {
|
|
179
|
+
return this.realPath
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Returns the virtual path relative to the cap.
|
|
184
|
+
* This is the default path representation in the capped environment.
|
|
185
|
+
* Use `.real.path` to access the actual filesystem path.
|
|
186
|
+
*
|
|
187
|
+
* @returns {string} Path relative to cap, or "/" if at cap root
|
|
188
|
+
* @example
|
|
189
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
190
|
+
* const subdir = temp.getDirectory("data/cache")
|
|
191
|
+
* console.log(subdir.path) // "/data/cache" (virtual, relative to cap)
|
|
192
|
+
* console.log(subdir.real.path) // "/tmp/myapp-ABC123/data/cache" (actual filesystem)
|
|
193
|
+
*/
|
|
194
|
+
get path() {
|
|
195
|
+
const capResolved = path.resolve(this.#cap)
|
|
196
|
+
const relative = path.relative(capResolved, this.#realPath)
|
|
197
|
+
|
|
198
|
+
// If at cap root or empty, return "/"
|
|
199
|
+
if(!relative || relative === ".") {
|
|
200
|
+
return "/"
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Return with leading slash to indicate it's cap-relative
|
|
204
|
+
return "/" + relative.split(path.sep).join("/")
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Returns a plain DirectoryObject representing the actual filesystem location.
|
|
209
|
+
* This provides an "escape hatch" from the capped environment to interact
|
|
210
|
+
* with the real filesystem when needed.
|
|
211
|
+
*
|
|
212
|
+
* @returns {DirectoryObject} Uncapped directory object at the real filesystem path
|
|
213
|
+
* @example
|
|
214
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
215
|
+
* const subdir = temp.getDirectory("data")
|
|
216
|
+
*
|
|
217
|
+
* // Work within the capped environment (virtual paths)
|
|
218
|
+
* console.log(subdir.path) // "/data" (virtual)
|
|
219
|
+
* subdir.getFile("config.json") // Stays within cap
|
|
220
|
+
*
|
|
221
|
+
* // Break out to real filesystem when needed
|
|
222
|
+
* console.log(subdir.real.path) // "/tmp/myapp-ABC123/data" (real)
|
|
223
|
+
* subdir.real.parent // Can traverse outside the cap
|
|
224
|
+
*/
|
|
225
|
+
get real() {
|
|
226
|
+
return new DirectoryObject(this.#realPath)
|
|
227
|
+
}
|
|
228
|
+
|
|
160
229
|
/**
|
|
161
230
|
* Returns the parent directory of this capped directory.
|
|
162
231
|
* Returns null only if this directory is at the cap (the "root" of the capped tree).
|
|
@@ -175,12 +244,17 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
175
244
|
const capResolved = path.resolve(this.#cap)
|
|
176
245
|
|
|
177
246
|
// If we're at the cap, return null (cap is the "root")
|
|
178
|
-
if(this
|
|
247
|
+
if(this.#realPath === capResolved) {
|
|
179
248
|
return null
|
|
180
249
|
}
|
|
181
250
|
|
|
182
|
-
// Otherwise return the parent (plain DirectoryObject, not capped)
|
|
183
|
-
|
|
251
|
+
// Otherwise return the parent using real path (plain DirectoryObject, not capped)
|
|
252
|
+
const parentPath = path.dirname(this.#realPath)
|
|
253
|
+
const isRoot = parentPath === this.#realPath
|
|
254
|
+
|
|
255
|
+
return isRoot
|
|
256
|
+
? null
|
|
257
|
+
: new DirectoryObject(parentPath, this.temporary)
|
|
184
258
|
}
|
|
185
259
|
|
|
186
260
|
/**
|
|
@@ -201,19 +275,27 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
201
275
|
*#walkUpCapped() {
|
|
202
276
|
const capResolved = path.resolve(this.#cap)
|
|
203
277
|
|
|
204
|
-
//
|
|
205
|
-
|
|
278
|
+
// Build trail from real path
|
|
279
|
+
const trail = this.#realPath.split(path.sep).filter(Boolean)
|
|
280
|
+
const curr = [...trail]
|
|
281
|
+
|
|
282
|
+
while(curr.length > 0) {
|
|
283
|
+
const joined = path.sep + curr.join(path.sep)
|
|
284
|
+
|
|
206
285
|
// Don't yield anything beyond the cap
|
|
207
|
-
if(!
|
|
286
|
+
if(!joined.startsWith(capResolved)) {
|
|
208
287
|
break
|
|
209
288
|
}
|
|
210
289
|
|
|
211
|
-
|
|
290
|
+
// Yield plain DirectoryObject with real path
|
|
291
|
+
yield new DirectoryObject(joined, this.temporary)
|
|
212
292
|
|
|
213
293
|
// Stop after yielding the cap
|
|
214
|
-
if(
|
|
294
|
+
if(joined === capResolved) {
|
|
215
295
|
break
|
|
216
296
|
}
|
|
297
|
+
|
|
298
|
+
curr.pop()
|
|
217
299
|
}
|
|
218
300
|
}
|
|
219
301
|
|
|
@@ -229,69 +311,286 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
229
311
|
/**
|
|
230
312
|
* Creates a new CappedDirectoryObject by extending this directory's path.
|
|
231
313
|
*
|
|
232
|
-
*
|
|
314
|
+
* All paths are coerced to remain within the cap directory tree:
|
|
315
|
+
* - Absolute paths (e.g., "/foo") are treated as relative to the cap
|
|
316
|
+
* - Parent traversal ("..") is allowed but clamped at the cap boundary
|
|
317
|
+
* - The cap acts as the virtual root directory
|
|
233
318
|
*
|
|
234
|
-
* @param {string} newPath - The path
|
|
235
|
-
* @returns {CappedDirectoryObject} A new CappedDirectoryObject with the
|
|
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 (..)
|
|
319
|
+
* @param {string} newPath - The path to resolve (can be absolute or contain ..)
|
|
320
|
+
* @returns {CappedDirectoryObject} A new CappedDirectoryObject with the coerced path
|
|
239
321
|
* @example
|
|
240
322
|
* const capped = new TempDirectoryObject("myapp")
|
|
241
323
|
* const subDir = capped.getDirectory("data")
|
|
242
324
|
* console.log(subDir.path) // "/tmp/myapp-ABC123/data"
|
|
325
|
+
*
|
|
326
|
+
* @example
|
|
327
|
+
* // Absolute paths are relative to cap
|
|
328
|
+
* const abs = capped.getDirectory("/foo/bar")
|
|
329
|
+
* console.log(abs.path) // "/tmp/myapp-ABC123/foo/bar"
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* // Excessive .. traversal clamps to cap
|
|
333
|
+
* const up = capped.getDirectory("../../../etc/passwd")
|
|
334
|
+
* console.log(up.path) // "/tmp/myapp-ABC123" (clamped to cap)
|
|
243
335
|
*/
|
|
244
336
|
getDirectory(newPath) {
|
|
245
337
|
Valid.type(newPath, "String")
|
|
246
338
|
|
|
247
|
-
//
|
|
339
|
+
// Fast path: if it's a simple name (no separators, not absolute, no ..)
|
|
340
|
+
// use the subclass constructor directly to preserve type
|
|
341
|
+
const isSimpleName = !path.isAbsolute(newPath) &&
|
|
342
|
+
!newPath.includes("/") &&
|
|
343
|
+
!newPath.includes("\\") &&
|
|
344
|
+
!newPath.includes("..")
|
|
345
|
+
|
|
346
|
+
if(isSimpleName) {
|
|
347
|
+
// For CappedDirectoryObject, pass (name, cap, parent, temporary)
|
|
348
|
+
// For TempDirectoryObject subclass, it expects (name, parent) but will
|
|
349
|
+
// internally call super with the cap parameter
|
|
350
|
+
if(this.constructor === CappedDirectoryObject) {
|
|
351
|
+
return new CappedDirectoryObject(
|
|
352
|
+
newPath,
|
|
353
|
+
this.#cap,
|
|
354
|
+
this,
|
|
355
|
+
this.temporary
|
|
356
|
+
)
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// For subclasses like TempDirectoryObject
|
|
360
|
+
return new this.constructor(newPath, this)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Complex path - handle coercion
|
|
364
|
+
const capResolved = path.resolve(this.#cap)
|
|
365
|
+
let targetPath
|
|
366
|
+
|
|
367
|
+
// If absolute, treat as relative to cap (virtual root)
|
|
248
368
|
if(path.isAbsolute(newPath)) {
|
|
249
|
-
|
|
369
|
+
// Strip leading slashes to make relative
|
|
370
|
+
const relative = newPath.replace(/^[/\\]+/, "")
|
|
371
|
+
|
|
372
|
+
// Join with cap (unless empty, which means cap root)
|
|
373
|
+
targetPath = relative ? path.join(capResolved, relative) : capResolved
|
|
374
|
+
} else {
|
|
375
|
+
// Relative path - resolve from current directory
|
|
376
|
+
targetPath = FS.resolvePath(this.#realPath, newPath)
|
|
250
377
|
}
|
|
251
378
|
|
|
252
|
-
//
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
379
|
+
// Resolve to absolute path (handles .. and .)
|
|
380
|
+
const resolved = path.resolve(targetPath)
|
|
381
|
+
|
|
382
|
+
// Coerce: if path escaped cap, clamp to cap boundary
|
|
383
|
+
const coerced = resolved.startsWith(capResolved)
|
|
384
|
+
? resolved
|
|
385
|
+
: capResolved
|
|
386
|
+
|
|
387
|
+
// Compute path relative to cap for reconstruction
|
|
388
|
+
const relativeToCap = path.relative(capResolved, coerced)
|
|
389
|
+
|
|
390
|
+
// If we're at the cap root, return cap root directory
|
|
391
|
+
if(!relativeToCap || relativeToCap === ".") {
|
|
392
|
+
return this.#createCappedAtRoot()
|
|
256
393
|
}
|
|
257
394
|
|
|
258
|
-
//
|
|
259
|
-
|
|
260
|
-
|
|
395
|
+
// Build directory by traversing segments from cap
|
|
396
|
+
return this.#buildDirectoryFromRelativePath(relativeToCap)
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Creates a CappedDirectoryObject at the cap root.
|
|
401
|
+
* Can be overridden by subclasses that have different root semantics.
|
|
402
|
+
*
|
|
403
|
+
* @private
|
|
404
|
+
* @returns {CappedDirectoryObject} Directory object at cap root
|
|
405
|
+
*/
|
|
406
|
+
#createCappedAtRoot() {
|
|
407
|
+
// Create a base CappedDirectoryObject at the cap path
|
|
408
|
+
// This works for direct usage of CappedDirectoryObject
|
|
409
|
+
// Subclasses may need to override if they have special semantics
|
|
410
|
+
return new CappedDirectoryObject(null, this.#cap, null, this.temporary)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Builds a directory by traversing path segments from cap.
|
|
415
|
+
*
|
|
416
|
+
* @private
|
|
417
|
+
* @param {string} relativePath - Path relative to cap
|
|
418
|
+
* @returns {CappedDirectoryObject} The directory at the final path
|
|
419
|
+
*/
|
|
420
|
+
#buildDirectoryFromRelativePath(relativePath) {
|
|
421
|
+
const segments = relativePath.split(path.sep).filter(Boolean)
|
|
422
|
+
|
|
423
|
+
// Start at cap root
|
|
424
|
+
let current = this.#createCappedAtRoot()
|
|
425
|
+
|
|
426
|
+
// Traverse each segment, creating CappedDirectoryObject instances
|
|
427
|
+
// (not subclass instances, to avoid constructor signature issues)
|
|
428
|
+
for(const segment of segments) {
|
|
429
|
+
current = new CappedDirectoryObject(
|
|
430
|
+
segment,
|
|
431
|
+
this.#cap,
|
|
432
|
+
current,
|
|
433
|
+
this.temporary
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return current
|
|
261
438
|
}
|
|
262
439
|
|
|
263
440
|
/**
|
|
264
441
|
* Creates a new FileObject by extending this directory's path.
|
|
265
442
|
*
|
|
266
|
-
*
|
|
443
|
+
* All paths are coerced to remain within the cap directory tree:
|
|
444
|
+
* - Absolute paths (e.g., "/config.json") are treated as relative to the cap
|
|
445
|
+
* - Parent traversal ("..") is allowed but clamped at the cap boundary
|
|
446
|
+
* - The cap acts as the virtual root directory
|
|
267
447
|
*
|
|
268
|
-
* @param {string} filename - The filename to
|
|
269
|
-
* @returns {FileObject} A new FileObject with the
|
|
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 (..)
|
|
448
|
+
* @param {string} filename - The filename to resolve (can be absolute or contain ..)
|
|
449
|
+
* @returns {FileObject} A new FileObject with the coerced path
|
|
273
450
|
* @example
|
|
274
451
|
* const capped = new TempDirectoryObject("myapp")
|
|
275
452
|
* const file = capped.getFile("config.json")
|
|
276
453
|
* console.log(file.path) // "/tmp/myapp-ABC123/config.json"
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* // Absolute paths are relative to cap
|
|
457
|
+
* const abs = capped.getFile("/data/config.json")
|
|
458
|
+
* console.log(abs.path) // "/tmp/myapp-ABC123/data/config.json"
|
|
459
|
+
*
|
|
460
|
+
* @example
|
|
461
|
+
* // Excessive .. traversal clamps to cap
|
|
462
|
+
* const up = capped.getFile("../../../etc/passwd")
|
|
463
|
+
* console.log(up.path) // "/tmp/myapp-ABC123/passwd" (clamped to cap)
|
|
277
464
|
*/
|
|
278
465
|
getFile(filename) {
|
|
279
466
|
Valid.type(filename, "String")
|
|
280
467
|
|
|
281
|
-
//
|
|
468
|
+
// Fast path: if it's a simple filename (no separators, not absolute, no ..)
|
|
469
|
+
// use this as the parent directly
|
|
470
|
+
const isSimpleName = !path.isAbsolute(filename) &&
|
|
471
|
+
!filename.includes("/") &&
|
|
472
|
+
!filename.includes("\\") &&
|
|
473
|
+
!filename.includes("..")
|
|
474
|
+
|
|
475
|
+
if(isSimpleName) {
|
|
476
|
+
// Simple filename - create directly with this as parent
|
|
477
|
+
return new FileObject(filename, this)
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Complex path - handle coercion
|
|
481
|
+
const capResolved = path.resolve(this.#cap)
|
|
482
|
+
let targetPath
|
|
483
|
+
|
|
484
|
+
// If absolute, treat as relative to cap (virtual root)
|
|
282
485
|
if(path.isAbsolute(filename)) {
|
|
283
|
-
|
|
486
|
+
// Strip leading slashes to make relative
|
|
487
|
+
const relative = filename.replace(/^[/\\]+/, "")
|
|
488
|
+
|
|
489
|
+
// Join with cap
|
|
490
|
+
targetPath = path.join(capResolved, relative)
|
|
491
|
+
} else {
|
|
492
|
+
// Relative path - resolve from current directory
|
|
493
|
+
targetPath = FS.resolvePath(this.#realPath, filename)
|
|
284
494
|
}
|
|
285
495
|
|
|
286
|
-
//
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
496
|
+
// Resolve to absolute path (handles .. and .)
|
|
497
|
+
const resolved = path.resolve(targetPath)
|
|
498
|
+
|
|
499
|
+
// Coerce: if path escaped cap, clamp to cap boundary
|
|
500
|
+
const coerced = resolved.startsWith(capResolved)
|
|
501
|
+
? resolved
|
|
502
|
+
: capResolved
|
|
503
|
+
|
|
504
|
+
// Extract directory and filename parts
|
|
505
|
+
let fileDir = path.dirname(coerced)
|
|
506
|
+
let fileBasename = path.basename(coerced)
|
|
507
|
+
|
|
508
|
+
// Special case: if coerced is exactly the cap (file tried to escape),
|
|
509
|
+
// the file should be placed at the cap root with just the filename
|
|
510
|
+
if(coerced === capResolved) {
|
|
511
|
+
// Extract just the filename from the original path
|
|
512
|
+
fileBasename = path.basename(resolved)
|
|
513
|
+
fileDir = capResolved
|
|
290
514
|
}
|
|
291
515
|
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
516
|
+
// Get or create the parent directory
|
|
517
|
+
const relativeToCap = path.relative(capResolved, fileDir)
|
|
518
|
+
const parentDir = !relativeToCap || relativeToCap === "."
|
|
519
|
+
? this.#createCappedAtRoot()
|
|
520
|
+
: this.#buildDirectoryFromRelativePath(relativeToCap)
|
|
521
|
+
|
|
522
|
+
// Create FileObject with parent directory
|
|
523
|
+
return new FileObject(fileBasename, parentDir)
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Override exists to use real filesystem path.
|
|
528
|
+
*
|
|
529
|
+
* @returns {Promise<boolean>} Whether the directory exists
|
|
530
|
+
*/
|
|
531
|
+
get exists() {
|
|
532
|
+
return this.real.exists
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Override read to use real filesystem path and return capped objects.
|
|
537
|
+
*
|
|
538
|
+
* @param {string} [pat=""] - Optional glob pattern
|
|
539
|
+
* @returns {Promise<{files: Array<FileObject>, directories: Array}>} Directory contents
|
|
540
|
+
*/
|
|
541
|
+
async read(pat="") {
|
|
542
|
+
const {files, directories} = await this.real.read(pat)
|
|
543
|
+
|
|
544
|
+
// Convert plain DirectoryObjects to CappedDirectoryObjects with same cap
|
|
545
|
+
const cappedDirectories = directories.map(dir => {
|
|
546
|
+
const name = dir.name
|
|
547
|
+
|
|
548
|
+
return new this.constructor(name, this)
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
return {files, directories: cappedDirectories}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
/**
|
|
555
|
+
* Override assureExists to use real filesystem path.
|
|
556
|
+
*
|
|
557
|
+
* @param {object} [options] - Options for mkdir
|
|
558
|
+
* @returns {Promise<void>}
|
|
559
|
+
*/
|
|
560
|
+
async assureExists(options = {}) {
|
|
561
|
+
return await this.real.assureExists(options)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Override delete to use real filesystem path.
|
|
566
|
+
*
|
|
567
|
+
* @returns {Promise<void>}
|
|
568
|
+
*/
|
|
569
|
+
async delete() {
|
|
570
|
+
return await this.real.delete()
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Override remove to preserve temporary flag check.
|
|
575
|
+
*
|
|
576
|
+
* @returns {Promise<void>}
|
|
577
|
+
*/
|
|
578
|
+
async remove() {
|
|
579
|
+
if(!this.temporary)
|
|
580
|
+
throw Sass.new("This is not a temporary directory.")
|
|
581
|
+
|
|
582
|
+
const {files, directories} = await this.read()
|
|
583
|
+
|
|
584
|
+
// Remove subdirectories recursively
|
|
585
|
+
for(const dir of directories)
|
|
586
|
+
await dir.remove()
|
|
587
|
+
|
|
588
|
+
// Remove files
|
|
589
|
+
for(const file of files)
|
|
590
|
+
await file.delete()
|
|
591
|
+
|
|
592
|
+
// Delete the now-empty directory
|
|
593
|
+
await this.delete()
|
|
295
594
|
}
|
|
296
595
|
|
|
297
596
|
/**
|
|
@@ -300,6 +599,6 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
300
599
|
* @returns {string} string representation of the CappedDirectoryObject
|
|
301
600
|
*/
|
|
302
601
|
toString() {
|
|
303
|
-
return `[CappedDirectoryObject: ${this.path}]`
|
|
602
|
+
return `[CappedDirectoryObject: ${this.path} (real: ${this.#realPath})]`
|
|
304
603
|
}
|
|
305
604
|
}
|
package/src/lib/FileObject.js
CHANGED
|
@@ -85,12 +85,10 @@ export default class FileObject extends FS {
|
|
|
85
85
|
if(Data.isType(fileName, "FileObject"))
|
|
86
86
|
fileName = fileName.path
|
|
87
87
|
|
|
88
|
-
if(!fileName || typeof fileName !== "string" || fileName.length === 0)
|
|
88
|
+
if(!fileName || typeof fileName !== "string" || fileName.length === 0)
|
|
89
89
|
throw Sass.new("fileName must be a non-empty string")
|
|
90
|
-
}
|
|
91
90
|
|
|
92
91
|
const fixedFile = FS.fixSlashes(fileName)
|
|
93
|
-
|
|
94
92
|
const {dir,base,ext} = this.#deconstructFilenameToParts(fixedFile)
|
|
95
93
|
|
|
96
94
|
const parentObject = (() => {
|
|
@@ -98,6 +96,7 @@ export default class FileObject extends FS {
|
|
|
98
96
|
case "String":
|
|
99
97
|
return new DirectoryObject(parent)
|
|
100
98
|
case "DirectoryObject":
|
|
99
|
+
case "CappedDirectoryObject":
|
|
101
100
|
case "TempDirectoryObject":
|
|
102
101
|
return parent
|
|
103
102
|
default:
|
|
@@ -105,7 +104,9 @@ export default class FileObject extends FS {
|
|
|
105
104
|
}
|
|
106
105
|
})()
|
|
107
106
|
|
|
108
|
-
|
|
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)
|
|
109
110
|
|
|
110
111
|
const resolved = final
|
|
111
112
|
const url = new URL(FS.pathToUri(resolved))
|
|
@@ -115,7 +116,9 @@ export default class FileObject extends FS {
|
|
|
115
116
|
|
|
116
117
|
// If the file is directly in the provided parent directory, reuse that object
|
|
117
118
|
// Otherwise, create a DirectoryObject for the actual parent directory
|
|
118
|
-
|
|
119
|
+
// Use real path for comparison if parent is capped
|
|
120
|
+
const parentRealPath = parentObject.realPath || parentObject.path
|
|
121
|
+
const actualParent = parentObject && actualParentPath === parentRealPath
|
|
119
122
|
? parentObject
|
|
120
123
|
: new DirectoryObject(actualParentPath)
|
|
121
124
|
|
|
@@ -186,12 +189,28 @@ export default class FileObject extends FS {
|
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
/**
|
|
189
|
-
*
|
|
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.
|
|
190
195
|
*
|
|
191
|
-
* @returns {string} The
|
|
196
|
+
* @returns {string} The file path (virtual if parent is capped, real otherwise)
|
|
192
197
|
*/
|
|
193
198
|
get path() {
|
|
194
|
-
|
|
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
|
|
195
214
|
}
|
|
196
215
|
|
|
197
216
|
/**
|
|
@@ -266,6 +285,26 @@ export default class FileObject extends FS {
|
|
|
266
285
|
return this.#meta.parent
|
|
267
286
|
}
|
|
268
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
|
+
|
|
269
308
|
/**
|
|
270
309
|
* Check if a file can be read. Returns true if the file can be read, false
|
|
271
310
|
*
|
|
@@ -273,7 +312,7 @@ export default class FileObject extends FS {
|
|
|
273
312
|
*/
|
|
274
313
|
async canRead() {
|
|
275
314
|
try {
|
|
276
|
-
await fs.access(this.path, fs.constants.R_OK)
|
|
315
|
+
await fs.access(this.#meta.path, fs.constants.R_OK)
|
|
277
316
|
|
|
278
317
|
return true
|
|
279
318
|
} catch(_) {
|
|
@@ -288,7 +327,7 @@ export default class FileObject extends FS {
|
|
|
288
327
|
*/
|
|
289
328
|
async canWrite() {
|
|
290
329
|
try {
|
|
291
|
-
await fs.access(this.path, fs.constants.W_OK)
|
|
330
|
+
await fs.access(this.#meta.path, fs.constants.W_OK)
|
|
292
331
|
|
|
293
332
|
return true
|
|
294
333
|
} catch(_) {
|
|
@@ -303,7 +342,7 @@ export default class FileObject extends FS {
|
|
|
303
342
|
*/
|
|
304
343
|
async #fileExists() {
|
|
305
344
|
try {
|
|
306
|
-
await fs.access(this.path, fs.constants.F_OK)
|
|
345
|
+
await fs.access(this.#meta.path, fs.constants.F_OK)
|
|
307
346
|
|
|
308
347
|
return true
|
|
309
348
|
} catch(_) {
|
|
@@ -318,7 +357,7 @@ export default class FileObject extends FS {
|
|
|
318
357
|
*/
|
|
319
358
|
async size() {
|
|
320
359
|
try {
|
|
321
|
-
const stat = await fs.stat(this.path)
|
|
360
|
+
const stat = await fs.stat(this.#meta.path)
|
|
322
361
|
|
|
323
362
|
return stat.size
|
|
324
363
|
} catch(_) {
|
|
@@ -334,7 +373,7 @@ export default class FileObject extends FS {
|
|
|
334
373
|
*/
|
|
335
374
|
async modified() {
|
|
336
375
|
try {
|
|
337
|
-
const stat = await fs.stat(this.path)
|
|
376
|
+
const stat = await fs.stat(this.#meta.path)
|
|
338
377
|
|
|
339
378
|
return stat.mtime
|
|
340
379
|
} catch(_) {
|
|
@@ -86,12 +86,12 @@ export default class TempDirectoryObject extends CappedDirectoryObject {
|
|
|
86
86
|
*/
|
|
87
87
|
#createDirectory() {
|
|
88
88
|
try {
|
|
89
|
-
fs.mkdirSync(this.
|
|
89
|
+
fs.mkdirSync(this.realPath)
|
|
90
90
|
} catch(e) {
|
|
91
91
|
// EEXIST is fine - directory already exists
|
|
92
92
|
if(e.code !== "EEXIST") {
|
|
93
93
|
throw Sass.new(
|
|
94
|
-
`Unable to create temporary directory '${this.
|
|
94
|
+
`Unable to create temporary directory '${this.realPath}': ${e.message}`
|
|
95
95
|
)
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { default as Collection } from "./lib/Collection.js";
|
|
2
2
|
export { default as Data } from "./lib/Data.js";
|
|
3
3
|
export { default as Notify } from "./lib/Notify.js";
|
|
4
|
+
export { default as Promised } from "./lib/Promised.js";
|
|
4
5
|
export { default as Sass } from "./lib/Sass.js";
|
|
5
6
|
export { default as Tantrum } from "./lib/Tantrum.js";
|
|
6
7
|
export { default as Type } from "./lib/TypeSpec.js";
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility class providing helper functions for working with Promises,
|
|
3
|
+
* including settling, filtering, and extracting values from promise results.
|
|
4
|
+
*/
|
|
5
|
+
export default class Promised {
|
|
6
|
+
/**
|
|
7
|
+
* Asynchronously awaits all promises in parallel.
|
|
8
|
+
* Wrapper around Promise.all for consistency with other utility methods.
|
|
9
|
+
*
|
|
10
|
+
* @param {Array<Promise<unknown>>} promises - Array of promises to await
|
|
11
|
+
* @returns {Promise<Array<unknown>>} Results of all promises
|
|
12
|
+
*/
|
|
13
|
+
static await(promises: Array<Promise<unknown>>): Promise<Array<unknown>>;
|
|
14
|
+
/**
|
|
15
|
+
* Settles all promises (both fulfilled and rejected) in parallel.
|
|
16
|
+
* Wrapper around Promise.allSettled for consistency with other utility methods.
|
|
17
|
+
*
|
|
18
|
+
* @param {Array<Promise<unknown>>} promises - Array of promises to settle
|
|
19
|
+
* @returns {Promise<Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>>} Results of all settled promises with status and value/reason
|
|
20
|
+
*/
|
|
21
|
+
static settle(promises: Array<Promise<unknown>>): Promise<Array<{
|
|
22
|
+
status: "fulfilled" | "rejected";
|
|
23
|
+
value?: unknown;
|
|
24
|
+
reason?: unknown;
|
|
25
|
+
}>>;
|
|
26
|
+
/**
|
|
27
|
+
* Checks if any result in the settled promise array is rejected.
|
|
28
|
+
*
|
|
29
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
30
|
+
* @returns {boolean} True if any result is rejected, false otherwise
|
|
31
|
+
*/
|
|
32
|
+
static hasRejected(settled: Array<{
|
|
33
|
+
status: "fulfilled" | "rejected";
|
|
34
|
+
value?: unknown;
|
|
35
|
+
reason?: unknown;
|
|
36
|
+
}>): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Checks if any result in the settled promise array is fulfilled.
|
|
39
|
+
*
|
|
40
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
41
|
+
* @returns {boolean} True if any result is fulfilled, false otherwise
|
|
42
|
+
*/
|
|
43
|
+
static hasFulfilled(settled: Array<{
|
|
44
|
+
status: "fulfilled" | "rejected";
|
|
45
|
+
value?: unknown;
|
|
46
|
+
reason?: unknown;
|
|
47
|
+
}>): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Filters and returns all rejected results from a settled promise array.
|
|
50
|
+
*
|
|
51
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
52
|
+
* @returns {Array<{status: 'rejected', reason: unknown}>} Array of rejected results
|
|
53
|
+
*/
|
|
54
|
+
static rejected(settled: Array<{
|
|
55
|
+
status: "fulfilled" | "rejected";
|
|
56
|
+
value?: unknown;
|
|
57
|
+
reason?: unknown;
|
|
58
|
+
}>): Array<{
|
|
59
|
+
status: "rejected";
|
|
60
|
+
reason: unknown;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Filters and returns all fulfilled results from a settled promise array.
|
|
64
|
+
*
|
|
65
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} result - Array of settled promise results
|
|
66
|
+
* @returns {Array<{status: 'fulfilled', value: unknown}>} Array of fulfilled results
|
|
67
|
+
*/
|
|
68
|
+
static fulfilled(result: Array<{
|
|
69
|
+
status: "fulfilled" | "rejected";
|
|
70
|
+
value?: unknown;
|
|
71
|
+
reason?: unknown;
|
|
72
|
+
}>): Array<{
|
|
73
|
+
status: "fulfilled";
|
|
74
|
+
value: unknown;
|
|
75
|
+
}>;
|
|
76
|
+
/**
|
|
77
|
+
* Extracts the rejection reasons from a settled promise array.
|
|
78
|
+
*
|
|
79
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
80
|
+
* @returns {Array<unknown>} Array of rejection reasons
|
|
81
|
+
*/
|
|
82
|
+
static reasons(settled: Array<{
|
|
83
|
+
status: "fulfilled" | "rejected";
|
|
84
|
+
value?: unknown;
|
|
85
|
+
reason?: unknown;
|
|
86
|
+
}>): Array<unknown>;
|
|
87
|
+
/**
|
|
88
|
+
* Extracts the values from fulfilled results in a settled promise array.
|
|
89
|
+
*
|
|
90
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
91
|
+
* @returns {Array<unknown>} Array of fulfilled values
|
|
92
|
+
*/
|
|
93
|
+
static values(settled: Array<{
|
|
94
|
+
status: "fulfilled" | "rejected";
|
|
95
|
+
value?: unknown;
|
|
96
|
+
reason?: unknown;
|
|
97
|
+
}>): Array<unknown>;
|
|
98
|
+
/**
|
|
99
|
+
* Throws a Tantrum containing all rejection reasons from settled promises.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} message - Error message. Defaults to "GIGO"
|
|
102
|
+
* @param {Array<{status: 'fulfilled'|'rejected', value?: unknown, reason?: unknown}>} settled - Array of settled promise results
|
|
103
|
+
* @throws {Tantrum} Throws a Tantrum error with rejection reasons
|
|
104
|
+
*/
|
|
105
|
+
static throw(message: string, settled: Array<{
|
|
106
|
+
status: "fulfilled" | "rejected";
|
|
107
|
+
value?: unknown;
|
|
108
|
+
reason?: unknown;
|
|
109
|
+
}>): void;
|
|
110
|
+
/**
|
|
111
|
+
* Returns the first promise to resolve or reject from an array of promises.
|
|
112
|
+
* Wrapper around Promise.race for consistency with other utility methods.
|
|
113
|
+
*
|
|
114
|
+
* @param {Array<Promise<unknown>>} promises - Array of promises to race
|
|
115
|
+
* @returns {Promise<unknown>} Result of the first settled promise
|
|
116
|
+
*/
|
|
117
|
+
static race(promises: Array<Promise<unknown>>): Promise<unknown>;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=Promised.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Promised.d.ts","sourceRoot":"","sources":["../../../browser/lib/Promised.js"],"names":[],"mappings":"AAEA;;;GAGG;AACH;IACE;;;;;;OAMG;IACH,uBAHW,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GACrB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAInC;IAED;;;;;;OAMG;IACH,wBAHW,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GACrB,OAAO,CAAC,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,CAAC,CAI/F;IAED;;;;;OAKG;IACH,4BAHW,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,GACxE,OAAO,CAInB;IAED;;;;;OAKG;IACH,6BAHW,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,GACxE,OAAO,CAInB;IAED;;;;;OAKG;IACH,yBAHW,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,GACxE,KAAK,CAAC;QAAC,MAAM,EAAE,UAAU,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAC,CAAC,CAIxD;IAED;;;;;OAKG;IACH,yBAHW,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,GACxE,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAC,CAAC,CAIxD;IAED;;;;;OAKG;IACH,wBAHW,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,GACxE,KAAK,CAAC,OAAO,CAAC,CAO1B;IAED;;;;;OAKG;IACH,uBAHW,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,GACxE,KAAK,CAAC,OAAO,CAAC,CAO1B;IAED;;;;;;OAMG;IACH,sBAJW,MAAM,WACN,KAAK,CAAC;QAAC,MAAM,EAAE,WAAW,GAAC,UAAU,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAC,CAAC,QAQpF;IAED;;;;;;OAMG;IACH,sBAHW,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GACrB,OAAO,CAAC,OAAO,CAAC,CAI5B;CACF"}
|
|
@@ -39,73 +39,6 @@ export default class Util {
|
|
|
39
39
|
* @returns {string} Padded string with text centred.
|
|
40
40
|
*/
|
|
41
41
|
static centreAlignText(text: string | number, width?: number): string;
|
|
42
|
-
/**
|
|
43
|
-
* Asynchronously awaits all promises in parallel.
|
|
44
|
-
* Wrapper around Promise.all for consistency with other utility methods.
|
|
45
|
-
*
|
|
46
|
-
* @param {Array<Promise<unknown>>} promises - Array of promises to await
|
|
47
|
-
* @returns {Promise<Array<unknown>>} Results of all promises
|
|
48
|
-
*/
|
|
49
|
-
static awaitAll(promises: Array<Promise<unknown>>): Promise<Array<unknown>>;
|
|
50
|
-
/**
|
|
51
|
-
* Settles all promises (both fulfilled and rejected) in parallel.
|
|
52
|
-
* Wrapper around Promise.allSettled for consistency with other utility methods.
|
|
53
|
-
*
|
|
54
|
-
* @param {Array<Promise<unknown>>} promises - Array of promises to settle
|
|
55
|
-
* @returns {Promise<Array<object>>} Results of all settled promises with status and value/reason
|
|
56
|
-
*/
|
|
57
|
-
static settleAll(promises: Array<Promise<unknown>>): Promise<Array<object>>;
|
|
58
|
-
/**
|
|
59
|
-
* Checks if any result in the settled promise array is rejected.
|
|
60
|
-
*
|
|
61
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
62
|
-
* @returns {boolean} True if any result is rejected, false otherwise.
|
|
63
|
-
*/
|
|
64
|
-
static anyRejected(result: Array<object>): boolean;
|
|
65
|
-
/**
|
|
66
|
-
* Filters and returns all rejected results from a settled promise array.
|
|
67
|
-
*
|
|
68
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
69
|
-
* @returns {Array<object>} Array of rejected results.
|
|
70
|
-
*/
|
|
71
|
-
static settledAndRejected(result: Array<object>): Array<object>;
|
|
72
|
-
/**
|
|
73
|
-
* Extracts the rejection reasons from an array of rejected promise results.
|
|
74
|
-
*
|
|
75
|
-
* @param {Array<object>} rejected - Array of rejected results.
|
|
76
|
-
* @returns {Array<unknown>} Array of rejection reasons.
|
|
77
|
-
*/
|
|
78
|
-
static rejectedReasons(rejected: Array<object>): Array<unknown>;
|
|
79
|
-
/**
|
|
80
|
-
* Throws a Sass error containing all rejection reasons from settled promises.
|
|
81
|
-
*
|
|
82
|
-
* @param {string} [_message] - Optional error message. Defaults to "GIGO"
|
|
83
|
-
* @param {Array<object>} rejected - Array of rejected results.
|
|
84
|
-
* @throws {Error} Throws a Tantrum error with rejection reasons.
|
|
85
|
-
*/
|
|
86
|
-
static throwRejected(message: string, settled: any): void;
|
|
87
|
-
/**
|
|
88
|
-
* Filters and returns all fulfilled results from a settled promise array.
|
|
89
|
-
*
|
|
90
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
91
|
-
* @returns {Array<object>} Array of fulfilled results.
|
|
92
|
-
*/
|
|
93
|
-
static settledAndFulfilled(result: Array<object>): Array<object>;
|
|
94
|
-
/**
|
|
95
|
-
* Extracts the values from all fulfilled results in a settled promise array.
|
|
96
|
-
*
|
|
97
|
-
* @param {Array<object>} result - Array of settled promise results.
|
|
98
|
-
* @returns {Array<unknown>} Array of fulfilled values.
|
|
99
|
-
*/
|
|
100
|
-
static fulfilledValues(result: Array<object>): Array<unknown>;
|
|
101
|
-
/**
|
|
102
|
-
* Returns the first promise to resolve or reject from an array of promises.
|
|
103
|
-
* Wrapper around Promise.race for consistency with other utility methods.
|
|
104
|
-
*
|
|
105
|
-
* @param {Array<Promise<unknown>>} promises - Array of promises to race
|
|
106
|
-
* @returns {Promise<unknown>} Result of the first settled promise
|
|
107
|
-
*/
|
|
108
|
-
static race(promises: Array<Promise<unknown>>): Promise<unknown>;
|
|
109
42
|
/**
|
|
110
43
|
* Determine the Levenshtein distance between two string values
|
|
111
44
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Util.d.ts","sourceRoot":"","sources":["../../../browser/lib/Util.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"Util.d.ts","sourceRoot":"","sources":["../../../browser/lib/Util.js"],"names":[],"mappings":"AAGA;;;GAGG;AACH;IACE;;;;;OAKG;IACH,wBAHW,MAAM,GACJ,MAAM,CAYlB;IAED;;;;;;OAMG;IACH,YAJa,CAAC,MACH,MAAM,OAAO,CAAC,CAAC,CAAC,GACd,OAAO,CAAC;QAAC,MAAM,EAAE,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,CAAC,CAQ9C;IAED;;;;;;;OAOG;IACH,4BAJW,MAAM,GAAC,MAAM,UACb,MAAM,GACJ,MAAM,CAWlB;IAED;;;;;;;OAOG;IACH,6BAJW,MAAM,GAAC,MAAM,UACb,MAAM,GACJ,MAAM,CAalB;IAED;;;;;;OAMG;IACH,8BAJW,MAAM,KACN,MAAM,GACJ,MAAM,CAsBlB;IAED;;;;;;;;OAQG;IACH,+BALW,MAAM,iBACN,KAAK,CAAC,MAAM,CAAC,cACb,MAAM,GACJ,MAAM,CAwBlB;IAED,mEAiBC;CACF"}
|
package/src/types/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as Collection } from "./browser/lib/Collection.js";
|
|
2
2
|
export { default as Data } from "./browser/lib/Data.js";
|
|
3
|
+
export { default as Promised } from "./browser/lib/Promised.js";
|
|
3
4
|
export { default as Type } from "./browser/lib/TypeSpec.js";
|
|
4
5
|
export { default as Valid } from "./lib/Valid.js";
|
|
5
6
|
export { default as Sass } from "./lib/Sass.js";
|
|
@@ -38,6 +38,32 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
38
38
|
* @returns {boolean} Always true for CappedDirectoryObject instances
|
|
39
39
|
*/
|
|
40
40
|
get capped(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Returns the real filesystem path (for internal and subclass use).
|
|
43
|
+
*
|
|
44
|
+
* @protected
|
|
45
|
+
* @returns {string} The actual filesystem path
|
|
46
|
+
*/
|
|
47
|
+
protected get realPath(): string;
|
|
48
|
+
/**
|
|
49
|
+
* Returns a plain DirectoryObject representing the actual filesystem location.
|
|
50
|
+
* This provides an "escape hatch" from the capped environment to interact
|
|
51
|
+
* with the real filesystem when needed.
|
|
52
|
+
*
|
|
53
|
+
* @returns {DirectoryObject} Uncapped directory object at the real filesystem path
|
|
54
|
+
* @example
|
|
55
|
+
* const temp = new TempDirectoryObject("myapp")
|
|
56
|
+
* const subdir = temp.getDirectory("data")
|
|
57
|
+
*
|
|
58
|
+
* // Work within the capped environment (virtual paths)
|
|
59
|
+
* console.log(subdir.path) // "/data" (virtual)
|
|
60
|
+
* subdir.getFile("config.json") // Stays within cap
|
|
61
|
+
*
|
|
62
|
+
* // Break out to real filesystem when needed
|
|
63
|
+
* console.log(subdir.real.path) // "/tmp/myapp-ABC123/data" (real)
|
|
64
|
+
* subdir.real.parent // Can traverse outside the cap
|
|
65
|
+
*/
|
|
66
|
+
get real(): DirectoryObject;
|
|
41
67
|
/**
|
|
42
68
|
* Returns a generator that walks up to the cap.
|
|
43
69
|
*
|
|
@@ -47,20 +73,41 @@ export default class CappedDirectoryObject extends DirectoryObject {
|
|
|
47
73
|
/**
|
|
48
74
|
* Creates a new CappedDirectoryObject by extending this directory's path.
|
|
49
75
|
*
|
|
50
|
-
*
|
|
76
|
+
* All paths are coerced to remain within the cap directory tree:
|
|
77
|
+
* - Absolute paths (e.g., "/foo") are treated as relative to the cap
|
|
78
|
+
* - Parent traversal ("..") is allowed but clamped at the cap boundary
|
|
79
|
+
* - The cap acts as the virtual root directory
|
|
51
80
|
*
|
|
52
|
-
* @param {string} newPath - The path
|
|
53
|
-
* @returns {CappedDirectoryObject} A new CappedDirectoryObject with the
|
|
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 (..)
|
|
81
|
+
* @param {string} newPath - The path to resolve (can be absolute or contain ..)
|
|
82
|
+
* @returns {CappedDirectoryObject} A new CappedDirectoryObject with the coerced path
|
|
57
83
|
* @example
|
|
58
84
|
* const capped = new TempDirectoryObject("myapp")
|
|
59
85
|
* const subDir = capped.getDirectory("data")
|
|
60
86
|
* console.log(subDir.path) // "/tmp/myapp-ABC123/data"
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* // Absolute paths are relative to cap
|
|
90
|
+
* const abs = capped.getDirectory("/foo/bar")
|
|
91
|
+
* console.log(abs.path) // "/tmp/myapp-ABC123/foo/bar"
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* // Excessive .. traversal clamps to cap
|
|
95
|
+
* const up = capped.getDirectory("../../../etc/passwd")
|
|
96
|
+
* console.log(up.path) // "/tmp/myapp-ABC123" (clamped to cap)
|
|
61
97
|
*/
|
|
62
98
|
getDirectory(newPath: string): CappedDirectoryObject;
|
|
99
|
+
/**
|
|
100
|
+
* Override read to use real filesystem path and return capped objects.
|
|
101
|
+
*
|
|
102
|
+
* @param {string} [pat=""] - Optional glob pattern
|
|
103
|
+
* @returns {Promise<{files: Array<FileObject>, directories: Array}>} Directory contents
|
|
104
|
+
*/
|
|
105
|
+
read(pat?: string): Promise<{
|
|
106
|
+
files: Array<FileObject>;
|
|
107
|
+
directories: any[];
|
|
108
|
+
}>;
|
|
63
109
|
#private;
|
|
64
110
|
}
|
|
65
111
|
import DirectoryObject from "./DirectoryObject.js";
|
|
112
|
+
import FileObject from "./FileObject.js";
|
|
66
113
|
//# sourceMappingURL=CappedDirectoryObject.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CappedDirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/CappedDirectoryObject.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CappedDirectoryObject.d.ts","sourceRoot":"","sources":["../../lib/CappedDirectoryObject.js"],"names":[],"mappings":"AAkBA;;;;;;;;GAQG;AACH;IAGE;;;;;;;;;;;;;;;;OAgBG;IACH,kBAXW,MAAM,OAAC,OACP,MAAM,WACN,qBAAqB,OAAC,cACtB,OAAO,EAmFjB;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,CA6EjC;IA0ID;;;;;OAKG;IACH,WAHW,MAAM,GACJ,OAAO,CAAC;QAAC,KAAK,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAAC,WAAW,QAAO;KAAC,CAAC,CAanE;;CAoDF;4BA9kB2B,sBAAsB;uBAC3B,iBAAiB"}
|
|
@@ -50,9 +50,11 @@ export default class FileObject extends FS {
|
|
|
50
50
|
*/
|
|
51
51
|
get supplied(): string;
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
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
|
|
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;
|
|
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"}
|