@gesslar/toolkit 0.0.1

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.
@@ -0,0 +1,207 @@
1
+ /**
2
+ * @file Type specification and validation utilities.
3
+ * Provides TypeSpec class for parsing and validating complex type specifications
4
+ * including arrays, unions, and options.
5
+ */
6
+
7
+ import Sass from "./Sass.js"
8
+ import Data from "./Data.js"
9
+
10
+ /**
11
+ * Type specification class for parsing and validating complex type definitions.
12
+ * Supports union types, array types, and validation options.
13
+ */
14
+ export default class TypeSpec {
15
+ #specs
16
+
17
+ /**
18
+ * Creates a new TypeSpec instance.
19
+ *
20
+ * @param {string} string - The type specification string (e.g., "string|number", "object[]")
21
+ * @param {object} options - Additional parsing options
22
+ */
23
+ constructor(string, options) {
24
+ this.#specs = []
25
+ this.#parse(string, options)
26
+ Object.freeze(this.#specs)
27
+ this.specs = this.#specs
28
+ this.length = this.#specs.length
29
+ this.stringRepresentation = this.toString()
30
+ Object.freeze(this)
31
+ }
32
+
33
+ /**
34
+ * Returns a string representation of the type specification.
35
+ *
36
+ * @returns {string} The type specification as a string (e.g., "string|number[]")
37
+ */
38
+ toString() {
39
+ return this.#specs
40
+ .map(spec => {
41
+ return `${spec.typeName}${spec.array ? "[]" : ""}`
42
+ })
43
+ .join("|")
44
+ }
45
+
46
+ /**
47
+ * Returns a JSON representation of the TypeSpec.
48
+ *
49
+ * @returns {object} Object containing specs, length, and string representation
50
+ */
51
+ toJSON() {
52
+ // Serialize as a string representation or as raw data
53
+ return {
54
+ specs: this.#specs,
55
+ length: this.length,
56
+ stringRepresentation: this.toString(),
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Executes a provided function once for each type specification.
62
+ *
63
+ * @param {function(unknown): void} callback - Function to execute for each spec
64
+ */
65
+ forEach(callback) {
66
+ this.#specs.forEach(callback)
67
+ }
68
+
69
+ /**
70
+ * Tests whether all type specifications pass the provided test function.
71
+ *
72
+ * @param {function(unknown): boolean} callback - Function to test each spec
73
+ * @returns {boolean} True if all specs pass the test
74
+ */
75
+ every(callback) {
76
+ return this.#specs.every(callback)
77
+ }
78
+
79
+ /**
80
+ * Tests whether at least one type specification passes the provided test function.
81
+ *
82
+ * @param {function(unknown): boolean} callback - Function to test each spec
83
+ * @returns {boolean} True if at least one spec passes the test
84
+ */
85
+ some(callback) {
86
+ return this.#specs.some(callback)
87
+ }
88
+
89
+ /**
90
+ * Creates a new array with all type specifications that pass the provided test function.
91
+ *
92
+ * @param {function(unknown): boolean} callback - Function to test each spec
93
+ * @returns {Array} New array with filtered specs
94
+ */
95
+ filter(callback) {
96
+ return this.#specs.filter(callback)
97
+ }
98
+
99
+ /**
100
+ * Creates a new array populated with the results of calling the provided function on every spec.
101
+ *
102
+ * @param {function(unknown): unknown} callback - Function to call on each spec
103
+ * @returns {Array} New array with mapped values
104
+ */
105
+ map(callback) {
106
+ return this.#specs.map(callback)
107
+ }
108
+
109
+ /**
110
+ * Executes a reducer function on each spec, resulting in a single output value.
111
+ *
112
+ * @param {function(unknown, unknown): unknown} callback - Function to execute on each spec
113
+ * @param {unknown} initialValue - Initial value for the accumulator
114
+ * @returns {unknown} The final accumulated value
115
+ */
116
+ reduce(callback, initialValue) {
117
+ return this.#specs.reduce(callback, initialValue)
118
+ }
119
+
120
+ /**
121
+ * Returns the first type specification that satisfies the provided testing function.
122
+ *
123
+ * @param {function(unknown): boolean} callback - Function to test each spec
124
+ * @returns {object|undefined} The first spec that matches, or undefined
125
+ */
126
+ find(callback) {
127
+ return this.#specs.find(callback)
128
+ }
129
+
130
+ /**
131
+ * Tests whether a value matches any of the type specifications.
132
+ * Handles array types, union types, and empty value validation.
133
+ *
134
+ * @param {unknown} value - The value to test against the type specifications
135
+ * @param {object} options - Validation options
136
+ * @param {boolean} options.allowEmpty - Whether empty values are allowed
137
+ * @returns {boolean} True if the value matches any type specification
138
+ */
139
+ match(value, options) {
140
+ const allowEmpty = options?.allowEmpty ?? true
141
+ const empty = Data.isEmpty(value)
142
+
143
+ // If we have a list of types, because the string was validly parsed,
144
+ // we need to ensure that all of the types that were parsed are valid types
145
+ // in JavaScript.
146
+ if(this.length && !this.every(t => Data.isValidType(t.typeName)))
147
+ return false
148
+
149
+ // Now, let's do some checking with the types, respecting the array flag
150
+ // with the value
151
+ const valueType = Data.typeOf(value)
152
+ const isArray = valueType === "array"
153
+
154
+ // We need to ensure that we match the type and the consistency of the types
155
+ // in an array, if it is an array and an array is allowed.
156
+ const matchingTypeSpec = this.filter(spec => {
157
+ const {typeName: allowedType, array: allowedArray} = spec
158
+
159
+ if(valueType === allowedType && !isArray && !allowedArray)
160
+ return !allowEmpty ? !empty : true
161
+
162
+ if(isArray) {
163
+ if(allowedType === "array")
164
+ if(!allowedArray)
165
+ return true
166
+
167
+ if(empty)
168
+ if(allowEmpty)
169
+ return true
170
+
171
+ return Data.isArrayUniform(value, allowedType)
172
+ }
173
+ })
174
+
175
+ return matchingTypeSpec.length > 0
176
+ }
177
+
178
+ /**
179
+ * Parses a type specification string into individual type specs.
180
+ * Handles union types separated by delimiters and array notation.
181
+ *
182
+ * @private
183
+ * @param {string} string - The type specification string to parse
184
+ * @param {object} options - Parsing options
185
+ * @param {string} options.delimiter - The delimiter for union types
186
+ * @throws {TypeError} If the type specification is invalid
187
+ */
188
+ #parse(string, options) {
189
+ const delimiter = options?.delimiter ?? "|"
190
+ const parts = string.split(delimiter)
191
+
192
+ this.#specs = parts.map(part => {
193
+ const typeMatches = /(\w+)(\[\])?/.exec(part)
194
+
195
+ if(!typeMatches || typeMatches.length !== 3)
196
+ throw Sass.new(`Invalid type: ${part}`)
197
+
198
+ if(!Data.isValidType(typeMatches[1]))
199
+ throw Sass.new(`Invalid type: ${typeMatches[1]}`)
200
+
201
+ return {
202
+ typeName: typeMatches[1],
203
+ array: typeMatches[2] === "[]",
204
+ }
205
+ })
206
+ }
207
+ }
@@ -0,0 +1,50 @@
1
+ import _assert from "node:assert/strict"
2
+
3
+ import Sass from "./Sass.js"
4
+ import Data from "./Data.js"
5
+
6
+ export default class Valid {
7
+ /**
8
+ * Validates a value against a type
9
+ *
10
+ * @param {unknown} value - The value to validate
11
+ * @param {string} type - The expected type in the form of "object",
12
+ * "object[]", "object|object[]"
13
+ * @param {object} [options] - Additional options for validation.
14
+ */
15
+ static validType(value, type, options) {
16
+ Valid.assert(
17
+ Data.isType(value, type, options),
18
+ `Invalid type. Expected ${type}, got ${JSON.stringify(value)}`,
19
+ 1,
20
+ )
21
+ }
22
+
23
+ /**
24
+ * Asserts a condition
25
+ *
26
+ * @param {boolean} condition - The condition to assert
27
+ * @param {string} message - The message to display if the condition is not
28
+ * met
29
+ * @param {number} [arg] - The argument to display if the condition is not
30
+ * met (optional)
31
+ */
32
+ static assert(condition, message, arg = null) {
33
+ _assert(
34
+ Data.isType(condition, "boolean"),
35
+ `Condition must be a boolean, got ${condition}`,
36
+ )
37
+ _assert(
38
+ Data.isType(message, "string"),
39
+ `Message must be a string, got ${message}`,
40
+ )
41
+ _assert(
42
+ arg === null || arg === undefined || typeof arg === "number",
43
+ `Arg must be a number, got ${arg}`,
44
+ )
45
+
46
+ if(!condition)
47
+ throw Sass.new(`${message}${arg ? `: ${arg}` : ""}`)
48
+ }
49
+
50
+ }
@@ -0,0 +1,96 @@
1
+ // Type definitions for Data utilities
2
+
3
+ import Type from './Type.js'
4
+
5
+ /**
6
+ * Data utility functions for type checking, object manipulation, and array operations.
7
+ */
8
+ export default class Data {
9
+ /** Array of JavaScript primitive type names */
10
+ static readonly primitives: readonly string[]
11
+
12
+ /** Array of JavaScript constructor names for built-in objects */
13
+ static readonly constructors: readonly string[]
14
+
15
+ /** Combined array of all supported data types */
16
+ static readonly dataTypes: readonly string[]
17
+
18
+ /** Array of type names that can be checked for emptiness */
19
+ static readonly emptyableTypes: readonly string[]
20
+
21
+ /** Append a string if it doesn't already end with it */
22
+ static appendString(string: string, append: string): string
23
+
24
+ /** Prepend a string if it doesn't already start with it */
25
+ static prependString(string: string, prepend: string): string
26
+
27
+ /** Check if all elements in an array are of a specified type */
28
+ static isArrayUniform(arr: unknown[], type?: string): boolean
29
+
30
+ /** Remove duplicates from an array */
31
+ static isArrayUnique<T>(arr: T[]): T[]
32
+
33
+ /** Get the intersection of two arrays */
34
+ static arrayIntersection<T>(arr1: T[], arr2: T[]): T[]
35
+
36
+ /** Check if two arrays have any elements in common */
37
+ static arrayIntersects<T>(arr1: T[], arr2: T[]): boolean
38
+
39
+ /** Pad an array to a specified length */
40
+ static arrayPad<T>(arr: T[], length: number, value: T, position?: number): T[]
41
+
42
+ /** Clone an object */
43
+ static cloneObject<T extends Record<string, any>>(obj: T, freeze?: boolean): T
44
+
45
+ /** Allocate an object from a source array and spec */
46
+ static allocateObject(source: unknown[], spec: unknown[] | ((source: unknown[]) => unknown[])): Promise<Record<string, unknown>>
47
+
48
+ /** Map an object using a transformer function */
49
+ static mapObject<T extends Record<string, any>, R>(
50
+ original: T,
51
+ transformer: (key: string, value: any) => R | Promise<R>,
52
+ mutate?: boolean
53
+ ): Promise<Record<string, R>>
54
+
55
+ /** Check if an object is empty */
56
+ static isObjectEmpty(obj: Record<string, any>): boolean
57
+
58
+ /** Create a type spec from a string */
59
+ static newTypeSpec(string: string, options?: any): Type
60
+
61
+ /** Check if a value is of a specified type */
62
+ static isType(value: unknown, type: string | Type, options?: { allowEmpty?: boolean }): boolean
63
+
64
+ /** Check if a type is valid */
65
+ static isValidType(type: string): boolean
66
+
67
+ /** Check if a value is of a base type (primitive or constructor) */
68
+ static isBaseType(value: unknown, type: string): boolean
69
+
70
+ /** Get the type of a value */
71
+ static typeOf(value: unknown): string
72
+
73
+ /** Check if a value is undefined or null */
74
+ static isNothing(value: unknown): value is null | undefined
75
+
76
+ /** Check if a value is empty */
77
+ static isEmpty(value: unknown, checkForNothing?: boolean): boolean
78
+
79
+ /** Recursively freeze an object */
80
+ static deepFreezeObject<T>(obj: T): T
81
+
82
+ /** Ensure a nested path of objects exists */
83
+ static assureObjectPath(obj: Record<string, any>, keys: string[]): Record<string, any>
84
+
85
+ /** Set a value in a nested object structure */
86
+ static setNestedValue(obj: Record<string, any>, keys: string[], value: unknown): void
87
+
88
+ /** Deeply merge objects */
89
+ static mergeObject<T extends Record<string, any>>(...sources: T[]): T
90
+
91
+ /** Check if all elements in an array are strings */
92
+ static uniformStringArray(arr: unknown[]): arr is string[]
93
+
94
+ /** Filter an array asynchronously */
95
+ static asyncFilter<T>(arr: T[], predicate: (item: T) => Promise<boolean>): Promise<T[]>
96
+ }
@@ -0,0 +1,55 @@
1
+ // Type definitions for DirectoryObject
2
+
3
+ /**
4
+ * DirectoryObject encapsulates metadata and operations for a directory,
5
+ * including path resolution and existence checks.
6
+ */
7
+ export default class DirectoryObject {
8
+ /**
9
+ * Create a new DirectoryObject instance.
10
+ * @param directory - The directory path
11
+ */
12
+ constructor(directory: string)
13
+
14
+ /** User-supplied path */
15
+ readonly supplied: string
16
+
17
+ /** The absolute directory path */
18
+ readonly path: string
19
+
20
+ /** The directory URI */
21
+ readonly uri: string
22
+
23
+ /** The directory name */
24
+ readonly name: string
25
+
26
+ /** The directory name without extension */
27
+ readonly module: string
28
+
29
+ /** The directory extension (usually empty) */
30
+ readonly extension: string
31
+
32
+ /** Always false for directories */
33
+ readonly isFile: false
34
+
35
+ /** Always true for directories */
36
+ readonly isDirectory: true
37
+
38
+ /** Whether the directory exists (async) */
39
+ readonly exists: Promise<boolean>
40
+
41
+ /** Returns a string representation of the DirectoryObject */
42
+ toString(): string
43
+
44
+ /** Returns a JSON representation of the DirectoryObject */
45
+ toJSON(): {
46
+ supplied: string
47
+ path: string
48
+ uri: string
49
+ name: string
50
+ module: string
51
+ extension: string
52
+ isFile: boolean
53
+ isDirectory: boolean
54
+ }
55
+ }
@@ -0,0 +1,76 @@
1
+ // Type definitions for File utilities
2
+
3
+ import FileObject from './FileObject.js'
4
+ import DirectoryObject from './DirectoryObject.js'
5
+
6
+ export interface FileParts {
7
+ /** The file name with extension */
8
+ base: string
9
+ /** The directory path */
10
+ dir: string
11
+ /** The file extension (including dot) */
12
+ ext: string
13
+ }
14
+
15
+ export interface DirectoryListing {
16
+ /** Array of FileObject instances */
17
+ files: FileObject[]
18
+ /** Array of DirectoryObject instances */
19
+ directories: DirectoryObject[]
20
+ }
21
+
22
+ /**
23
+ * File system utilities for reading, writing, and manipulating files and directories.
24
+ */
25
+ export default class File {
26
+ /** Fix slashes in a path */
27
+ static fixSlashes(pathName: string): string
28
+
29
+ /** Convert a path to a URI */
30
+ static pathToUri(pathName: string): string
31
+
32
+ /** Convert a URI to a path */
33
+ static uriToPath(pathName: string): string
34
+
35
+ /** Check if a file can be read */
36
+ static canReadFile(file: FileObject): Promise<boolean>
37
+
38
+ /** Check if a file can be written */
39
+ static canWriteFile(file: FileObject): Promise<boolean>
40
+
41
+ /** Check if a file exists */
42
+ static fileExists(file: FileObject): Promise<boolean>
43
+
44
+ /** Get the size of a file */
45
+ static fileSize(file: FileObject): Promise<number | null>
46
+
47
+ /** Get the last modification time of a file */
48
+ static fileModified(file: FileObject): Promise<Date | null>
49
+
50
+ /** Check if a directory exists */
51
+ static directoryExists(dirObject: DirectoryObject): Promise<boolean>
52
+
53
+ /** Deconstruct a filename into parts */
54
+ static deconstructFilenameToParts(fileName: string): FileParts
55
+
56
+ /** Retrieve files matching glob pattern(s) */
57
+ static getFiles(glob: string | string[]): Promise<FileObject[]>
58
+
59
+ /** List the contents of a directory */
60
+ static ls(directory: string): Promise<DirectoryListing>
61
+
62
+ /** Read the content of a file */
63
+ static readFile(fileObject: FileObject): Promise<string>
64
+
65
+ /** Write content to a file */
66
+ static writeFile(fileObject: FileObject, content: string): Promise<void>
67
+
68
+ /** Load an object from JSON or YAML file */
69
+ static loadDataFile(fileObject: FileObject): Promise<any>
70
+
71
+ /** Ensure a directory exists, creating it if necessary */
72
+ static assureDirectory(dirObject: DirectoryObject, options?: any): Promise<boolean>
73
+
74
+ /** Compute relative path between two file system objects */
75
+ static relativeOrAbsolutePath(from: FileObject | DirectoryObject, to: FileObject | DirectoryObject): string
76
+ }
@@ -0,0 +1,59 @@
1
+ // Type definitions for FileObject
2
+
3
+ import DirectoryObject from './DirectoryObject.js'
4
+
5
+ /**
6
+ * FileObject encapsulates metadata and operations for a file, including path
7
+ * resolution and existence checks.
8
+ */
9
+ export default class FileObject {
10
+ /**
11
+ * Create a new FileObject instance.
12
+ * @param fileName - The file path
13
+ * @param directory - The parent directory (object or string)
14
+ */
15
+ constructor(fileName: string, directory?: DirectoryObject | string | null)
16
+
17
+ /** User-supplied path */
18
+ readonly supplied: string
19
+
20
+ /** The absolute file path */
21
+ readonly path: string
22
+
23
+ /** The file URI */
24
+ readonly uri: string
25
+
26
+ /** The file name */
27
+ readonly name: string
28
+
29
+ /** The file name without extension */
30
+ readonly module: string
31
+
32
+ /** The file extension */
33
+ readonly extension: string
34
+
35
+ /** Always true for files */
36
+ readonly isFile: true
37
+
38
+ /** Always false for files */
39
+ readonly isDirectory: false
40
+
41
+ /** The parent directory object */
42
+ readonly directory: DirectoryObject
43
+
44
+ /** Whether the file exists (async) */
45
+ readonly exists: Promise<boolean>
46
+
47
+ /** Returns a JSON representation of the FileObject */
48
+ toJSON(): {
49
+ supplied: string
50
+ path: string
51
+ uri: string
52
+ name: string
53
+ module: string
54
+ extension: string
55
+ isFile: boolean
56
+ isDirectory: boolean
57
+ directory: string | null
58
+ }
59
+ }
@@ -0,0 +1,23 @@
1
+ // Type definitions for Sass error class
2
+
3
+ /**
4
+ * Custom error class for toolkit errors.
5
+ */
6
+ export default class Sass extends Error {
7
+ constructor(message: string, ...arg: any[])
8
+
9
+ /** Array of trace messages */
10
+ readonly trace: string[]
11
+
12
+ /** Add a trace message and return this instance for chaining */
13
+ addTrace(message: string): this
14
+
15
+ /** Report the error to the terminal */
16
+ report(nerdMode?: boolean): void
17
+
18
+ /** Create a Sass from an existing Error object */
19
+ static from(error: Error, message: string): Sass
20
+
21
+ /** Factory method to create or enhance Sass instances */
22
+ static new(message: string, error?: Error | Sass): Sass
23
+ }
@@ -0,0 +1,15 @@
1
+ // Type definitions for Term utility class
2
+
3
+ export default class Term {
4
+ static log(...arg: unknown[]): void
5
+ static info(...arg: unknown[]): void
6
+ static warn(...arg: unknown[]): void
7
+ static error(...arg: unknown[]): void
8
+ static debug(...arg: unknown[]): void
9
+ static status(args: string | Array<string | [string, string]>, options?: { silent?: boolean }): void
10
+ static terminalMessage(argList: string | Array<string | [string, string] | [string, string, string]>): string
11
+ static terminalBracket(parts: [string, string, [string, string]?]): string
12
+ static resetTerminal(): Promise<void>
13
+ static clearLines(num: number): Promise<void>
14
+ static directWrite(output: string): Promise<void>
15
+ }
@@ -0,0 +1,25 @@
1
+ // Type definitions for Type specification class
2
+
3
+ interface TypeSpecDefinition {
4
+ typeName: string
5
+ array: boolean
6
+ }
7
+
8
+ export default class TypeSpec {
9
+ constructor(string: string, options?: { delimiter?: string })
10
+
11
+ readonly specs: readonly TypeSpecDefinition[]
12
+ readonly length: number
13
+ readonly stringRepresentation: string
14
+
15
+ toString(): string
16
+ toJSON(): { specs: TypeSpecDefinition[], length: number, stringRepresentation: string }
17
+ forEach(callback: (spec: TypeSpecDefinition) => void): void
18
+ every(callback: (spec: TypeSpecDefinition) => boolean): boolean
19
+ some(callback: (spec: TypeSpecDefinition) => boolean): boolean
20
+ filter(callback: (spec: TypeSpecDefinition) => boolean): TypeSpecDefinition[]
21
+ map<T>(callback: (spec: TypeSpecDefinition) => T): T[]
22
+ reduce<T>(callback: (acc: T, spec: TypeSpecDefinition) => T, initialValue: T): T
23
+ find(callback: (spec: TypeSpecDefinition) => boolean): TypeSpecDefinition | undefined
24
+ match(value: unknown, options?: { allowEmpty?: boolean }): boolean
25
+ }
@@ -0,0 +1,9 @@
1
+ // Type definitions for Valid utility class
2
+
3
+ export default class Valid {
4
+ /** Validate a value against a type specification */
5
+ static validType(value: unknown, type: string, options?: { allowEmpty?: boolean }): void
6
+
7
+ /** Assert a condition */
8
+ static assert(condition: boolean, message: string, arg?: number | null): void
9
+ }
@@ -0,0 +1,14 @@
1
+ // Core file system abstractions
2
+ export { default as FileObject } from './FileObject.js'
3
+ export { default as DirectoryObject } from './DirectoryObject.js'
4
+ export { default as File } from './File.js'
5
+
6
+ // Utility classes
7
+ export { default as Data } from './Data.js'
8
+ export { default as Sass } from './Sass.js'
9
+ export { default as Term } from './Term.js'
10
+ export { default as Type } from './Type.js'
11
+ export { default as Valid } from './Valid.js'
12
+
13
+ // Type exports
14
+ export type { FileParts } from './File.js'