@gesslar/toolkit 0.8.0 → 1.0.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 +35 -0
- package/package.json +20 -8
- package/src/browser/index.js +10 -0
- package/src/browser/lib/Sass.js +168 -0
- package/src/browser/lib/Tantrum.js +115 -0
- package/src/browser/lib/Util.js +257 -0
- package/src/browser/lib/Valid.js +76 -0
- package/src/index.js +14 -12
- package/src/lib/Cache.js +2 -3
- package/src/lib/Contract.js +3 -4
- package/src/lib/FS.js +15 -20
- package/src/lib/FileObject.js +1 -1
- package/src/lib/Glog.js +2 -2
- package/src/lib/Sass.js +2 -91
- package/src/lib/Schemer.js +2 -2
- package/src/lib/Tantrum.js +3 -70
- package/src/lib/Terms.js +2 -3
- package/src/lib/Util.js +2 -252
- package/src/lib/Valid.js +17 -20
- package/src/types/browser/index.d.ts +8 -0
- package/src/types/browser/index.d.ts.map +1 -0
- package/src/types/browser/lib/Collection.d.ts +246 -0
- package/src/types/browser/lib/Collection.d.ts.map +1 -0
- package/src/types/browser/lib/Data.d.ts +206 -0
- package/src/types/browser/lib/Data.d.ts.map +1 -0
- package/src/types/browser/lib/Sass.d.ts +62 -0
- package/src/types/browser/lib/Sass.d.ts.map +1 -0
- package/src/types/browser/lib/Tantrum.d.ts +51 -0
- package/src/types/browser/lib/Tantrum.d.ts.map +1 -0
- package/src/types/browser/lib/TypeSpec.d.ts +92 -0
- package/src/types/browser/lib/TypeSpec.d.ts.map +1 -0
- package/src/types/browser/lib/Util.d.ts +129 -0
- package/src/types/browser/lib/Util.d.ts.map +1 -0
- package/src/types/browser/lib/Valid.d.ts +33 -0
- package/src/types/browser/lib/Valid.d.ts.map +1 -0
- package/src/types/index.d.ts +10 -10
- package/src/types/lib/FS.d.ts +2 -2
- package/src/types/lib/FS.d.ts.map +1 -1
- package/src/types/lib/Sass.d.ts +2 -55
- package/src/types/lib/Sass.d.ts.map +1 -1
- package/src/types/lib/Tantrum.d.ts +3 -44
- package/src/types/lib/Tantrum.d.ts.map +1 -1
- package/src/types/lib/Util.d.ts +2 -123
- package/src/types/lib/Util.d.ts.map +1 -1
- package/src/types/lib/Valid.d.ts +1 -1
- package/src/types/lib/Valid.d.ts.map +1 -1
- /package/src/{lib → browser/lib}/Collection.js +0 -0
- /package/src/{lib → browser/lib}/Data.js +0 -0
- /package/src/{lib → browser/lib}/TypeSpec.js +0 -0
package/README.md
CHANGED
|
@@ -7,6 +7,41 @@ but the kind that says "yumyum."
|
|
|
7
7
|
There are file and directory abstractions, uhmm, there's also some terminal
|
|
8
8
|
things and validity checkers, lots of data functions.
|
|
9
9
|
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
**For Node.js projects:**
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
import { Data, FileObject, Cache } from '@gesslar/toolkit'
|
|
16
|
+
// or explicitly:
|
|
17
|
+
import { Data, FileObject } from '@gesslar/toolkit/node'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**For browsers or Tauri apps:**
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
import { Data, Collection, Util } from '@gesslar/toolkit/browser'
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The browser version includes: Data, Collection, Util, Type (TypeSpec), Valid,
|
|
27
|
+
Sass, and Tantrum. Node-only modules (FileObject, Cache, FS, etc.) are not
|
|
28
|
+
available in the browser version.
|
|
29
|
+
|
|
30
|
+
**Browser via CDN (no install):**
|
|
31
|
+
|
|
32
|
+
- jsDelivr (runtime only):
|
|
33
|
+
`https://cdn.jsdelivr.net/npm/@gesslar/toolkit/browser`
|
|
34
|
+
- esm.sh (runtime + optional types):
|
|
35
|
+
`https://esm.sh/@gesslar/toolkit@0.8.0/browser`
|
|
36
|
+
`https://esm.sh/@gesslar/toolkit@0.8.0/browser?dts` (serves `.d.ts` for editors)
|
|
37
|
+
|
|
38
|
+
Notes:
|
|
39
|
+
|
|
40
|
+
- Nothing to configure in this repo for CDN use; both URLs just work.
|
|
41
|
+
- TypeScript editors do not pick up types from jsDelivr. If you want inline
|
|
42
|
+
types without installing from npm, use the esm.sh `?dts` URL or install the
|
|
43
|
+
package locally for development and use the CDN at runtime.
|
|
44
|
+
|
|
10
45
|
Basically, if you want it, it is most definitely here, and working 100% and
|
|
11
46
|
absolutely none of that is true. There are only a few classes here, but they're
|
|
12
47
|
pretty. And if you bug-shame them, I will _come for you like_ ...
|
package/package.json
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gesslar/toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Get in, bitches, we're going toolkitting.",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
+
"types": "./src/types/index.d.ts",
|
|
10
|
+
"browser": "./src/browser/index.js",
|
|
11
|
+
"node": "./src/index.js",
|
|
12
|
+
"default": "./src/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./browser": {
|
|
15
|
+
"types": "./src/types/index.d.ts",
|
|
16
|
+
"default": "./src/browser/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./node": {
|
|
9
19
|
"types": "./src/types/index.d.ts",
|
|
10
20
|
"default": "./src/index.js"
|
|
11
21
|
}
|
|
@@ -25,7 +35,7 @@
|
|
|
25
35
|
"lint:fix": "eslint src/ --fix",
|
|
26
36
|
"submit": "npm publish --access public",
|
|
27
37
|
"update": "npx npm-check-updates -u && npm install",
|
|
28
|
-
"test": "node --test tests
|
|
38
|
+
"test": "node --test tests/**/*.test.js",
|
|
29
39
|
"pr": "gt submit --publish --restack --ai"
|
|
30
40
|
},
|
|
31
41
|
"repository": {
|
|
@@ -52,16 +62,18 @@
|
|
|
52
62
|
"dependencies": {
|
|
53
63
|
"@gesslar/colours": "^0.0.1",
|
|
54
64
|
"ajv": "^8.17.1",
|
|
55
|
-
"globby": "^
|
|
65
|
+
"globby": "^16.0.0",
|
|
56
66
|
"json5": "^2.2.3",
|
|
57
67
|
"yaml": "^2.8.1"
|
|
58
68
|
},
|
|
59
69
|
"devDependencies": {
|
|
60
|
-
"@
|
|
61
|
-
"@
|
|
62
|
-
"@
|
|
63
|
-
"@typescript-eslint/
|
|
70
|
+
"@gesslar/uglier": "^0.0.5",
|
|
71
|
+
"@stylistic/eslint-plugin": "^5.6.1",
|
|
72
|
+
"@types/node": "^24.10.1",
|
|
73
|
+
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
|
74
|
+
"@typescript-eslint/parser": "^8.48.0",
|
|
64
75
|
"eslint": "^9.39.1",
|
|
65
|
-
"eslint-plugin-jsdoc": "^61.1
|
|
76
|
+
"eslint-plugin-jsdoc": "^61.4.1",
|
|
77
|
+
"globals": "^16.5.0"
|
|
66
78
|
}
|
|
67
79
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Browser-compatible utilities
|
|
2
|
+
// Pure JavaScript modules that work in browsers, Tauri, and Node.js
|
|
3
|
+
|
|
4
|
+
export {default as Collection} from "./lib/Collection.js"
|
|
5
|
+
export {default as Data} from "./lib/Data.js"
|
|
6
|
+
export {default as Sass} from "./lib/Sass.js"
|
|
7
|
+
export {default as Tantrum} from "./lib/Tantrum.js"
|
|
8
|
+
export {default as Type} from "./lib/TypeSpec.js"
|
|
9
|
+
export {default as Util} from "./lib/Util.js"
|
|
10
|
+
export {default as Valid} from "./lib/Valid.js"
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Sass.js
|
|
3
|
+
*
|
|
4
|
+
* Defines the Sass class, a custom error type for toolkit compilation
|
|
5
|
+
* errors.
|
|
6
|
+
*
|
|
7
|
+
* Supports error chaining, trace management, and formatted reporting for both
|
|
8
|
+
* user-friendly and verbose (nerd) output.
|
|
9
|
+
*
|
|
10
|
+
* Used throughout the toolkit for structured error handling and
|
|
11
|
+
* debugging.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import Tantrum from "./Tantrum.js"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Custom error class for toolkit errors.
|
|
18
|
+
* Provides error chaining, trace management, and formatted error reporting.
|
|
19
|
+
*/
|
|
20
|
+
export default class Sass extends Error {
|
|
21
|
+
#trace = []
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new Sass instance.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} message - The error message
|
|
27
|
+
* @param {...unknown} [arg] - Additional arguments passed to parent Error constructor
|
|
28
|
+
*/
|
|
29
|
+
constructor(message, ...arg) {
|
|
30
|
+
super(message, ...arg)
|
|
31
|
+
|
|
32
|
+
this.trace = message
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Gets the error trace array.
|
|
37
|
+
*
|
|
38
|
+
* @returns {Array<string>} Array of trace messages
|
|
39
|
+
*/
|
|
40
|
+
get trace() {
|
|
41
|
+
return this.#trace
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Adds a message to the beginning of the trace array.
|
|
46
|
+
*
|
|
47
|
+
* @param {string} message - The trace message to add
|
|
48
|
+
*/
|
|
49
|
+
set trace(message) {
|
|
50
|
+
this.#trace.unshift(message)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Adds a trace message and returns this instance for chaining.
|
|
55
|
+
*
|
|
56
|
+
* @param {string} message - The trace message to add
|
|
57
|
+
* @returns {this} This Sass instance for method chaining
|
|
58
|
+
*/
|
|
59
|
+
addTrace(message) {
|
|
60
|
+
if(typeof message !== "string")
|
|
61
|
+
throw this.constructor.new(`Sass.addTrace expected string, got ${JSON.stringify(message)}`)
|
|
62
|
+
|
|
63
|
+
this.trace = message
|
|
64
|
+
|
|
65
|
+
return this
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Reports the error to the console with formatted output.
|
|
70
|
+
* Optionally includes detailed stack trace information.
|
|
71
|
+
*
|
|
72
|
+
* @param {boolean} [nerdMode] - Whether to include detailed stack trace
|
|
73
|
+
*/
|
|
74
|
+
report(nerdMode=false) {
|
|
75
|
+
console.error(
|
|
76
|
+
`[error] Something Went Wrong\n` +
|
|
77
|
+
this.trace.join("\n")
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if(nerdMode) {
|
|
81
|
+
console.error(
|
|
82
|
+
"\n" +
|
|
83
|
+
`[error] Nerd Vittles\n` +
|
|
84
|
+
this.#fullBodyMassage(this.stack)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
this.cause?.stack && console.error(
|
|
88
|
+
"\n" +
|
|
89
|
+
`[error] Rethrown From\n` +
|
|
90
|
+
this.#fullBodyMassage(this.cause?.stack)
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Formats the stack trace for display, removing the first line and
|
|
97
|
+
* formatting each line with appropriate indentation.
|
|
98
|
+
*
|
|
99
|
+
* Note: Returns formatted stack trace or undefined if no stack available.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} stack - The error stack to massage.
|
|
102
|
+
* @returns {string|undefined} Formatted stack trace or undefined
|
|
103
|
+
*/
|
|
104
|
+
#fullBodyMassage(stack) {
|
|
105
|
+
stack = stack ?? ""
|
|
106
|
+
// Remove the first line, it's already been reported
|
|
107
|
+
const {rest} = stack.match(/^.*?\n(?<rest>[\s\S]+)$/m)?.groups ?? {}
|
|
108
|
+
const lines = []
|
|
109
|
+
|
|
110
|
+
if(rest) {
|
|
111
|
+
lines.push(
|
|
112
|
+
...rest
|
|
113
|
+
.split("\n")
|
|
114
|
+
.map(line => {
|
|
115
|
+
const at = line.match(/^\s{4}at\s(?<at>.*)$/)?.groups?.at ?? ""
|
|
116
|
+
|
|
117
|
+
return at
|
|
118
|
+
? `* ${at}`
|
|
119
|
+
: line
|
|
120
|
+
})
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return lines.join("\n")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Creates an Sass from an existing Error object with additional
|
|
129
|
+
* trace message.
|
|
130
|
+
*
|
|
131
|
+
* @param {Error} error - The original error object
|
|
132
|
+
* @param {string} message - Additional trace message to add
|
|
133
|
+
* @returns {Sass} New Sass instance with trace from the original error
|
|
134
|
+
* @throws {Sass} If the first parameter is not an Error instance
|
|
135
|
+
*/
|
|
136
|
+
static from(error, message) {
|
|
137
|
+
if(!(error instanceof Error))
|
|
138
|
+
throw this.new("Sass.from must take an Error object.")
|
|
139
|
+
|
|
140
|
+
const oldMessage = error.message
|
|
141
|
+
const newError = new this(oldMessage, {cause: error}).addTrace(message)
|
|
142
|
+
|
|
143
|
+
return newError
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Factory method to create or enhance Sass instances.
|
|
148
|
+
* If error parameter is provided, enhances existing Sass or wraps
|
|
149
|
+
* other errors. Otherwise creates a new Sass instance.
|
|
150
|
+
*
|
|
151
|
+
* @param {string} message - The error message
|
|
152
|
+
* @param {Error|Sass|Tantrum} [error] - Optional existing error to wrap or enhance
|
|
153
|
+
* @returns {Sass} New or enhanced Sass instance
|
|
154
|
+
*/
|
|
155
|
+
static new(message, error) {
|
|
156
|
+
if(error) {
|
|
157
|
+
if(error instanceof Tantrum)
|
|
158
|
+
return Tantrum.new(message, error)
|
|
159
|
+
|
|
160
|
+
return error instanceof this
|
|
161
|
+
? error.addTrace(message)
|
|
162
|
+
: this.from(error, message)
|
|
163
|
+
} else {
|
|
164
|
+
|
|
165
|
+
return new this(message)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tantrum.js
|
|
3
|
+
*
|
|
4
|
+
* Defines the Tantrum class, a custom AggregateError type for toolkit
|
|
5
|
+
* that collects multiple errors with Sass-style reporting.
|
|
6
|
+
*
|
|
7
|
+
* Auto-wraps plain Error objects in Sass instances while preserving
|
|
8
|
+
* existing Sass errors, providing consistent formatted output for
|
|
9
|
+
* multiple error scenarios.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import Sass from "./Sass.js"
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Custom aggregate error class that extends AggregateError.
|
|
16
|
+
* Automatically wraps plain errors in Sass instances for consistent reporting.
|
|
17
|
+
*/
|
|
18
|
+
export default class Tantrum extends AggregateError {
|
|
19
|
+
#trace = []
|
|
20
|
+
#sass
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new Tantrum instance.
|
|
24
|
+
*
|
|
25
|
+
* @param {string} message - The aggregate error message
|
|
26
|
+
* @param {Array<Error|Sass>} errors - Array of errors to aggregate
|
|
27
|
+
* @param {Sass} sass - Sass constructor
|
|
28
|
+
*/
|
|
29
|
+
constructor(message, errors = [], sass=Sass) {
|
|
30
|
+
// Auto-wrap plain errors in Sass, keep existing Sass instances
|
|
31
|
+
const wrappedErrors = errors.map(error => {
|
|
32
|
+
if(error instanceof sass)
|
|
33
|
+
return error
|
|
34
|
+
|
|
35
|
+
if(!(error instanceof Error))
|
|
36
|
+
throw new TypeError(`All items in errors array must be Error instances, got: ${typeof error}`)
|
|
37
|
+
|
|
38
|
+
return sass.new(error.message, error)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
super(wrappedErrors, message)
|
|
42
|
+
|
|
43
|
+
this.name = "Tantrum"
|
|
44
|
+
this.#sass = sass
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Adds a trace message and returns this instance for chaining.
|
|
49
|
+
*
|
|
50
|
+
* @param {string} message - The trace message to add
|
|
51
|
+
* @param {Error|Sass} [_error] - Optional error (currently unused, reserved for future use)
|
|
52
|
+
* @returns {this} This Tantrum instance for method chaining
|
|
53
|
+
*/
|
|
54
|
+
addTrace(message, _error) {
|
|
55
|
+
if(typeof message !== "string")
|
|
56
|
+
throw this.#sass.new(`Tantrum.addTrace expected string, got ${JSON.stringify(message)}`)
|
|
57
|
+
|
|
58
|
+
this.trace = message
|
|
59
|
+
|
|
60
|
+
return this
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Gets the error trace array.
|
|
65
|
+
*
|
|
66
|
+
* @returns {Array<string>} Array of trace messages
|
|
67
|
+
*/
|
|
68
|
+
get trace() {
|
|
69
|
+
return this.#trace
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Adds a message to the beginning of the trace array.
|
|
74
|
+
*
|
|
75
|
+
* @param {string} message - The trace message to add
|
|
76
|
+
*/
|
|
77
|
+
set trace(message) {
|
|
78
|
+
this.#trace.unshift(message)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Reports all aggregated errors to the console with formatted output.
|
|
83
|
+
*
|
|
84
|
+
* @param {boolean} [nerdMode] - Whether to include detailed stack traces
|
|
85
|
+
*/
|
|
86
|
+
report(nerdMode = false) {
|
|
87
|
+
console.error(
|
|
88
|
+
`[error] Tantrum Incoming (${this.errors.length} errors)\n` +
|
|
89
|
+
this.message
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if(this.trace)
|
|
93
|
+
console.error(this.trace.join("\n"))
|
|
94
|
+
|
|
95
|
+
console.error()
|
|
96
|
+
|
|
97
|
+
this.errors.forEach(error => {
|
|
98
|
+
error.report(nerdMode)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Factory method to create a Tantrum instance.
|
|
104
|
+
*
|
|
105
|
+
* @param {string} message - The aggregate error message
|
|
106
|
+
* @param {Array<Error|Sass>} errors - Array of errors to aggregate
|
|
107
|
+
* @returns {Tantrum} New Tantrum instance
|
|
108
|
+
*/
|
|
109
|
+
static new(message, errors = []) {
|
|
110
|
+
if(errors instanceof this)
|
|
111
|
+
return errors.addTrace(message)
|
|
112
|
+
|
|
113
|
+
return new this(message, errors)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import Sass from "./Sass.js"
|
|
2
|
+
import Valid from "./Valid.js"
|
|
3
|
+
import Collection from "./Collection.js"
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility class providing common helper functions for string manipulation,
|
|
7
|
+
* timing, and option parsing.
|
|
8
|
+
*/
|
|
9
|
+
export default class Util {
|
|
10
|
+
/**
|
|
11
|
+
* Capitalizes the first letter of a string.
|
|
12
|
+
*
|
|
13
|
+
* @param {string} text - The text to capitalize
|
|
14
|
+
* @returns {string} Text with first letter capitalized
|
|
15
|
+
*/
|
|
16
|
+
static capitalize(text) {
|
|
17
|
+
if(typeof text !== "string")
|
|
18
|
+
throw new TypeError("Util.capitalize expects a string")
|
|
19
|
+
|
|
20
|
+
if(text.length === 0)
|
|
21
|
+
return ""
|
|
22
|
+
|
|
23
|
+
const [first, ...rest] = Array.from(text)
|
|
24
|
+
|
|
25
|
+
return `${first.toLocaleUpperCase()}${rest.join("")}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Measure wall-clock time for an async function.
|
|
30
|
+
*
|
|
31
|
+
* @template T
|
|
32
|
+
* @param {() => Promise<T>} fn - Thunk returning a promise.
|
|
33
|
+
* @returns {Promise<{result: T, cost: number}>} Object containing result and elapsed ms (number, 1 decimal).
|
|
34
|
+
*/
|
|
35
|
+
static async time(fn) {
|
|
36
|
+
const t0 = performance.now()
|
|
37
|
+
const result = await fn()
|
|
38
|
+
const cost = Math.round((performance.now() - t0) * 10) / 10
|
|
39
|
+
|
|
40
|
+
return {result, cost}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Right-align a string inside a fixed width (left pad with spaces).
|
|
45
|
+
* If the string exceeds width it is returned unchanged.
|
|
46
|
+
*
|
|
47
|
+
* @param {string|number} text - Text to align.
|
|
48
|
+
* @param {number} width - Target field width (default 80).
|
|
49
|
+
* @returns {string} Padded string.
|
|
50
|
+
*/
|
|
51
|
+
static rightAlignText(text, width=80) {
|
|
52
|
+
const work = String(text)
|
|
53
|
+
|
|
54
|
+
if(work.length > width)
|
|
55
|
+
return work
|
|
56
|
+
|
|
57
|
+
const diff = width-work.length
|
|
58
|
+
|
|
59
|
+
return `${" ".repeat(diff)}${work}`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Centre-align a string inside a fixed width (pad with spaces on left).
|
|
64
|
+
* If the string exceeds width it is returned unchanged.
|
|
65
|
+
*
|
|
66
|
+
* @param {string|number} text - Text to align.
|
|
67
|
+
* @param {number} width - Target field width (default 80).
|
|
68
|
+
* @returns {string} Padded string with text centred.
|
|
69
|
+
*/
|
|
70
|
+
static centreAlignText(text, width=80) {
|
|
71
|
+
const work = String(text)
|
|
72
|
+
|
|
73
|
+
if(work.length >= width)
|
|
74
|
+
return work
|
|
75
|
+
|
|
76
|
+
const totalPadding = width - work.length
|
|
77
|
+
const leftPadding = Math.floor(totalPadding / 2)
|
|
78
|
+
const rightPadding = totalPadding - leftPadding
|
|
79
|
+
|
|
80
|
+
return `${" ".repeat(leftPadding)}${work}${" ".repeat(rightPadding)}`
|
|
81
|
+
}
|
|
82
|
+
|
|
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 Sass error with rejection reasons.
|
|
141
|
+
*/
|
|
142
|
+
static throwRejected(_message="GIGO", rejected) {
|
|
143
|
+
throw Sass.new(this.rejectedReasons(rejected))
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Filters and returns all fulfilled results from a settled promise array.
|
|
148
|
+
*
|
|
149
|
+
* @param {Array<object>} result - Array of settled promise results.
|
|
150
|
+
* @returns {Array<object>} Array of fulfilled results.
|
|
151
|
+
*/
|
|
152
|
+
static settledAndFulfilled(result) {
|
|
153
|
+
return result.filter(r => r.status === "fulfilled")
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Extracts the values from all fulfilled results in a settled promise array.
|
|
158
|
+
*
|
|
159
|
+
* @param {Array<object>} result - Array of settled promise results.
|
|
160
|
+
* @returns {Array<unknown>} Array of fulfilled values.
|
|
161
|
+
*/
|
|
162
|
+
static fulfilledValues(result) {
|
|
163
|
+
return this.settledAndFulfilled(result).map(r => r.value)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Returns the first promise to resolve or reject from an array of promises.
|
|
168
|
+
* Wrapper around Promise.race for consistency with other utility methods.
|
|
169
|
+
*
|
|
170
|
+
* @param {Array<Promise<unknown>>} promises - Array of promises to race
|
|
171
|
+
* @returns {Promise<unknown>} Result of the first settled promise
|
|
172
|
+
*/
|
|
173
|
+
static async race(promises) {
|
|
174
|
+
return await Promise.race(promises)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Determine the Levenshtein distance between two string values
|
|
179
|
+
*
|
|
180
|
+
* @param {string} a The first value for comparison.
|
|
181
|
+
* @param {string} b The second value for comparison.
|
|
182
|
+
* @returns {number} The Levenshtein distance
|
|
183
|
+
*/
|
|
184
|
+
static levenshteinDistance(a, b) {
|
|
185
|
+
const matrix = Array.from({length: a.length + 1}, (_, i) =>
|
|
186
|
+
Array.from({length: b.length + 1}, (_, j) =>
|
|
187
|
+
(i === 0 ? j : j === 0 ? i : 0)
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
for(let i = 1; i <= a.length; i++) {
|
|
192
|
+
for(let j = 1; j <= b.length; j++) {
|
|
193
|
+
matrix[i][j] =
|
|
194
|
+
a[i - 1] === b[j - 1]
|
|
195
|
+
? matrix[i - 1][j - 1]
|
|
196
|
+
: 1 + Math.min(
|
|
197
|
+
matrix[i - 1][j], matrix[i][j - 1],
|
|
198
|
+
matrix[i - 1][j - 1]
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return matrix[a.length][b.length]
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Determine the closest match between a string and allowed values
|
|
208
|
+
* from the Levenshtein distance.
|
|
209
|
+
*
|
|
210
|
+
* @param {string} input The input string to resolve
|
|
211
|
+
* @param {Array<string>} allowedValues The values which are permitted
|
|
212
|
+
* @param {number} [threshold] Max edit distance for a "close match"
|
|
213
|
+
* @returns {string} Suggested, probable match.
|
|
214
|
+
*/
|
|
215
|
+
static findClosestMatch(input, allowedValues, threshold=2) {
|
|
216
|
+
let closestMatch = null
|
|
217
|
+
let closestDistance = Infinity
|
|
218
|
+
let closestLengthDiff = Infinity
|
|
219
|
+
|
|
220
|
+
for(const value of allowedValues) {
|
|
221
|
+
const distance = this.levenshteinDistance(input, value)
|
|
222
|
+
const lengthDiff = Math.abs(input.length - value.length)
|
|
223
|
+
|
|
224
|
+
if(distance < closestDistance && distance <= threshold) {
|
|
225
|
+
closestMatch = value
|
|
226
|
+
closestDistance = distance
|
|
227
|
+
closestLengthDiff = lengthDiff
|
|
228
|
+
} else if(distance === closestDistance &&
|
|
229
|
+
distance <= threshold &&
|
|
230
|
+
lengthDiff < closestLengthDiff) {
|
|
231
|
+
closestMatch = value
|
|
232
|
+
closestLengthDiff = lengthDiff
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return closestMatch
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
static regexify(input, trim=true, flags=[]) {
|
|
240
|
+
Valid.type(input, "String")
|
|
241
|
+
Valid.type(trim, "Boolean")
|
|
242
|
+
Valid.type(flags, "Array")
|
|
243
|
+
|
|
244
|
+
Valid.assert(
|
|
245
|
+
flags.length === 0 ||
|
|
246
|
+
(flags.length > 0 && Collection.isArrayUniform(flags, "String")),
|
|
247
|
+
"All flags must be strings")
|
|
248
|
+
|
|
249
|
+
return new RegExp(
|
|
250
|
+
input
|
|
251
|
+
.split(/\r\n|\r|\n/)
|
|
252
|
+
.map(i => trim ? i.trim() : i)
|
|
253
|
+
.filter(i => trim ? Boolean(i) : true)
|
|
254
|
+
.join("")
|
|
255
|
+
, flags?.join(""))
|
|
256
|
+
}
|
|
257
|
+
}
|