@kikiutils/shared 20.1.1 → 20.1.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"path.js","names":[],"sources":["../../src/classes/path.ts"],"sourcesContent":["import type { Buffer } from 'node:buffer';\nimport type { Abortable } from 'node:events';\nimport type * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as nodePath from 'node:path';\n\ntype DropFirstParameters<T extends (...args: any) => any> = Parameters<T> extends [any, ...infer R] ? R : never;\n\n/**\n * Values accepted by the `Path` constructor and path-composition methods.\n */\nexport type PathLike = fs.PathLike | Path;\n\n/**\n * An immutable file system path with Node.js path and file operations.\n *\n * @remarks\n * Path-composition methods return new instances and leave this instance unchanged.\n * File operations act on the stored path and forward Node.js defaults and promise rejections.\n * They do not update the stored path, including after a successful rename.\n * The constructor joins stringified inputs using the platform-specific Node.js path rules.\n */\nexport class Path {\n // Private instance properties\n readonly #value: string;\n\n // Constructor\n\n /**\n * Creates a normalized path value by joining the provided path segments.\n *\n * @param paths - Path segments accepted by Node.js `path.join` or another `Path` instance.\n */\n constructor(...paths: PathLike[]) {\n this.#value = nodePath.join(...this.#toStrings(paths));\n }\n\n // Private instance methods\n #newInstance(...paths: PathLike[]) {\n return new Path(...paths);\n }\n\n #toStrings(paths: PathLike[]) {\n return paths.map((path) => path.toString());\n }\n\n // Public instance accessors\n\n /**\n * The parent directory as a new `Path`; this instance is unchanged.\n *\n * @see {@link Path.dirname}\n */\n get parent() {\n return this.dirname();\n }\n\n /**\n * The normalized path string stored by this instance.\n */\n get value() {\n return this.#value;\n }\n\n // Public static methods\n\n /**\n * Creates a new path from a Node.js path object.\n *\n * @param pathObject - The path components to format.\n *\n * @returns A new normalized `Path`.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.format}\n */\n static format(pathObject: nodePath.FormatInputPathObject) {\n return new Path(nodePath.format(pathObject));\n }\n\n /**\n * Resolves joined path segments to an absolute path.\n *\n * @remarks\n * Inputs are joined before resolution; unlike Node.js `path.resolve`,\n * later absolute segments do not reset the path.\n *\n * @param paths - The stringified path segments to join.\n *\n * @returns A new absolute `Path`.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.resolve}\n */\n static resolve(...paths: PathLike[]) {\n return new this(...paths).resolve();\n }\n\n // Public instance methods\n [Symbol.for('nodejs.util.inspect.custom')]() {\n return this.#value;\n }\n\n [Symbol.toPrimitive](hint: string) {\n if (hint === 'number') throw new TypeError('Cannot convert a Path to a number');\n return this.#value;\n }\n\n /**\n * Returns the final component of this path.\n *\n * @param suffix - An optional exact suffix to remove.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.basename}\n */\n basename(suffix?: string) {\n return nodePath.basename(this.#value, suffix);\n }\n\n /**\n * Returns the parent directory of this path.\n *\n * @returns A new `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.dirname}\n */\n dirname() {\n return this.#newInstance(nodePath.dirname(this.#value));\n }\n\n /**\n * Returns the extension of the final path component.\n *\n * @returns The extension including its leading dot, or an empty string when absent.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.extname}\n */\n extname() {\n return nodePath.extname(this.#value);\n }\n\n /**\n * Checks whether this path is absolute on the current platform.\n *\n * @returns Whether this path is absolute.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.isAbsolute}\n */\n isAbsolute() {\n return nodePath.isAbsolute(this.#value);\n }\n\n /**\n * Normalizes separators and relative segments in this path.\n *\n * @returns A new `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.normalize}\n */\n normalize() {\n return this.#newInstance(nodePath.normalize(this.#value));\n }\n\n /**\n * Joins additional segments onto this path.\n *\n * @param paths - The stringified path segments to append.\n *\n * @returns A new normalized `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.join}\n */\n join(...paths: PathLike[]) {\n return this.#newInstance(this.#value, ...this.#toStrings(paths));\n }\n\n /**\n * Parses this path into its root, directory, basename, extension, and name.\n *\n * @returns A new object containing the path components.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.parse}\n */\n parse() {\n return nodePath.parse(this.#value);\n }\n\n /**\n * Computes a relative path from this path to the destination.\n *\n * @param to - The destination path.\n *\n * @returns A new `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.relative}\n */\n relative(to: PathLike) {\n return this.#newInstance(nodePath.relative(this.#value, to.toString()));\n }\n\n /**\n * Resolves this path to an absolute path using the current working directory.\n *\n * @remarks\n * Relative paths use the current working directory. This instance is unchanged.\n *\n * @returns A new absolute `Path`.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.resolve}\n */\n resolve() {\n return this.#newInstance(nodePath.resolve(this.#value));\n }\n\n toJSON() {\n return this.#value;\n }\n\n /**\n * Converts this path to the Windows namespace form when applicable.\n *\n * @returns The namespace-converted path string; this instance is unchanged.\n * On non-Windows platforms, the path text is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.toNamespacedPath}\n */\n toNamespacedPath() {\n return nodePath.toNamespacedPath(this.#value);\n }\n\n /**\n * Returns the stored path as a string.\n */\n toString() {\n return this.#value;\n }\n\n /**\n * Checks access permissions for the stored path.\n *\n * @remarks\n * The promise resolves when the access check succeeds and rejects if access is denied or the path is missing.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.access}\n */\n async access(...args: DropFirstParameters<typeof fsp.access>) {\n return await fsp.access(this.#value, ...args);\n }\n\n /**\n * Appends data to the file at the stored path.\n *\n * @remarks\n * The promise resolves when appending completes; a missing file is created according to the supplied options.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.appendFile}\n */\n async appendFile(...args: DropFirstParameters<typeof fsp.appendFile>) {\n return await fsp.appendFile(this.#value, ...args);\n }\n\n /**\n * Changes the permissions of the stored path.\n *\n * @remarks\n * The promise resolves when the permissions are changed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.chmod}\n */\n async chmod(...args: DropFirstParameters<typeof fsp.chmod>) {\n return await fsp.chmod(this.#value, ...args);\n }\n\n /**\n * Changes the owner and group of the stored path.\n *\n * @remarks\n * The promise resolves when the ownership is changed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.chown}\n */\n async chown(...args: DropFirstParameters<typeof fsp.chown>) {\n return await fsp.chown(this.#value, ...args);\n }\n\n /**\n * Copies the file at the stored path to a destination.\n *\n * @remarks\n * The promise resolves when copying completes. The stored path remains the source path.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.copyFile}\n */\n async copyFile(...args: DropFirstParameters<typeof fsp.copyFile>) {\n return await fsp.copyFile(this.#value, ...args);\n }\n\n /**\n * Creates a directory at the stored path.\n *\n * @remarks\n * The promise resolves after creation. Recursive mode resolves to the first created directory path, or\n * `undefined` if no directory is created; nonrecursive mode resolves without a value.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.mkdir}\n */\n mkdir(options: fs.MakeDirectoryOptions & { recursive: true }): Promise<string | undefined>;\n mkdir(\n options?:\n | (fs.MakeDirectoryOptions & { recursive?: false })\n | fs.Mode\n | null,\n ): Promise<void>;\n mkdir(options?: fs.MakeDirectoryOptions | fs.Mode | null): Promise<string | undefined>;\n async mkdir(...args: any): Promise<any> {\n return await fsp.mkdir(this.#value, ...args);\n }\n\n /**\n * Opens the file at the stored path.\n *\n * @remarks\n * The promise resolves to a new file handle. The caller must close the handle when it is no longer needed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.open}\n */\n async open(...args: DropFirstParameters<typeof fsp.open>) {\n return await fsp.open(this.#value, ...args);\n }\n\n /**\n * Reads the entries of the directory at the stored path.\n *\n * @remarks\n * The promise resolves to names, buffers, or directory entries according to the options.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.readdir}\n */\n readdir(\n options?:\n | BufferEncoding\n | (fs.ObjectEncodingOptions & {\n recursive?: boolean;\n withFileTypes?: false;\n })\n | null,\n ): Promise<string[]>;\n readdir(\n options:\n | 'buffer'\n | {\n encoding: 'buffer';\n recursive?: boolean;\n withFileTypes?: false;\n },\n ): Promise<Buffer[]>;\n readdir(\n options?:\n | BufferEncoding\n | (fs.ObjectEncodingOptions & {\n recursive?: boolean;\n withFileTypes?: false;\n })\n | null,\n ): Promise<Buffer[] | string[]>;\n readdir(\n options: fs.ObjectEncodingOptions & {\n recursive?: boolean;\n withFileTypes: true;\n },\n ): Promise<fs.Dirent[]>;\n readdir(\n options: {\n encoding: 'buffer';\n recursive?: boolean;\n withFileTypes: true;\n },\n ): Promise<Array<fs.Dirent<Buffer>>>;\n async readdir(...args: any): Promise<any> {\n return await fsp.readdir(this.#value, ...args);\n }\n\n /**\n * Reads the entire file at the stored path.\n *\n * @remarks\n * The promise resolves to a string when a text encoding is supplied, or a `Buffer` otherwise.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.readFile}\n */\n readFile(\n options?:\n | (Abortable & {\n encoding?: null;\n flag?: fs.OpenMode;\n })\n | null,\n ): Promise<Buffer>;\n readFile(\n options:\n | (Abortable & {\n encoding: BufferEncoding;\n flag?: fs.OpenMode;\n })\n | BufferEncoding,\n ): Promise<string>;\n readFile(\n options?:\n | (\n & Abortable\n & fs.ObjectEncodingOptions\n & { flag?: fs.OpenMode }\n )\n | BufferEncoding\n | null,\n ): Promise<Buffer | string>;\n async readFile(...args: any): Promise<any> {\n return await fsp.readFile(this.#value, ...args);\n }\n\n /**\n * Renames or moves the file or directory at the stored path.\n *\n * @remarks\n * The promise resolves when the rename completes. This instance retains its original stored path.\n *\n * @param newPath - The destination path; this instance retains its original value.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.rename}\n */\n async rename(newPath: PathLike) {\n return await fsp.rename(this.#value, newPath.toString());\n }\n\n /**\n * Removes the file or directory at the stored path.\n *\n * @remarks\n * The promise resolves after removal according to the supplied options.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.rm}\n */\n async rm(...args: DropFirstParameters<typeof fsp.rm>) {\n return await fsp.rm(this.#value, ...args);\n }\n\n /**\n * Removes the directory at the stored path.\n *\n * @remarks\n * The promise resolves after directory removal.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.rmdir}\n */\n async rmdir(...args: DropFirstParameters<typeof fsp.rmdir>) {\n return await fsp.rmdir(this.#value, ...args);\n }\n\n /**\n * Reads file system metadata for the stored path.\n *\n * @remarks\n * The promise resolves to `Stats`, or `BigIntStats` when `bigint` is enabled.\n * If `throwIfNoEntry` is `false`, a missing path resolves to `undefined` instead of rejecting.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.stat}\n */\n stat(\n opts?: fs.StatOptions & {\n bigint?: false;\n throwIfNoEntry?: true;\n },\n ): Promise<fs.Stats>;\n stat(\n opts: fs.StatOptions & {\n bigint: true;\n throwIfNoEntry?: true;\n },\n ): Promise<fs.BigIntStats>;\n stat(\n opts: fs.StatOptions & {\n bigint?: false;\n throwIfNoEntry: false;\n },\n ): Promise<fs.Stats | undefined>;\n stat(\n opts: fs.StatOptions & {\n bigint: true;\n throwIfNoEntry: false;\n },\n ): Promise<fs.BigIntStats | undefined>;\n stat(opts: fs.StatOptions & { throwIfNoEntry?: true }): Promise<fs.BigIntStats | fs.Stats>;\n stat(opts?: fs.StatOptions): Promise<fs.BigIntStats | fs.Stats | undefined>;\n async stat(opts?: fs.StatOptions): Promise<fs.BigIntStats | fs.Stats | undefined> {\n try {\n return await fsp.stat(this.#value, opts);\n } catch (error) {\n if (\n opts?.throwIfNoEntry === false\n && error instanceof Error\n && 'code' in error\n && error.code === 'ENOENT'\n ) return undefined;\n\n throw error;\n }\n }\n\n /**\n * Truncates or extends the file at the stored path.\n *\n * @remarks\n * The promise resolves when the file reaches the requested length in bytes; the default length is `0`.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.truncate}\n */\n async truncate(...args: DropFirstParameters<typeof fsp.truncate>) {\n return await fsp.truncate(this.#value, ...args);\n }\n\n /**\n * Deletes the file or symbolic link at the stored path.\n *\n * @remarks\n * The promise resolves after deletion; directories are not removed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.unlink}\n */\n async unlink() {\n return await fsp.unlink(this.#value);\n }\n\n /**\n * Writes data to the file at the stored path.\n *\n * @remarks\n * The promise resolves when writing completes; the default flag replaces existing file contents.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.writeFile}\n */\n async writeFile(...args: DropFirstParameters<typeof fsp.writeFile>) {\n return await fsp.writeFile(this.#value, ...args);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"path.js","names":[],"sources":["../../src/classes/path.ts"],"sourcesContent":["import type { Buffer } from 'node:buffer';\nimport type { Abortable } from 'node:events';\nimport type * as fs from 'node:fs';\nimport * as fsp from 'node:fs/promises';\nimport * as nodePath from 'node:path';\n\ntype DropFirstParameters<T extends (...args: any) => any> = Parameters<T> extends [any, ...infer R] ? R : never;\n\n/**\n * Values accepted by the `Path` constructor and path-composition methods.\n */\nexport type PathLike = fs.PathLike | Path;\n\n/**\n * An immutable file system path with Node.js path and file operations.\n *\n * @remarks\n * Path-composition methods return new instances and leave this instance unchanged.\n * File operations act on the stored path and forward Node.js defaults and promise rejections.\n * They do not update the stored path, including after a successful rename.\n * The constructor joins stringified inputs using the platform-specific Node.js path rules.\n */\nexport class Path {\n // Private instance properties\n readonly #value: string;\n\n // Constructor\n\n /**\n * Creates a normalized path value by joining the provided path segments.\n *\n * @param paths - Path segments accepted by Node.js `path.join` or another `Path` instance.\n */\n constructor(...paths: PathLike[]) {\n this.#value = nodePath.join(...this.#toStrings(paths));\n }\n\n // Private instance methods\n #newInstance(...paths: PathLike[]) {\n return new Path(...paths);\n }\n\n #toStrings(paths: PathLike[]) {\n return paths.map((path) => path.toString());\n }\n\n // Public instance accessors\n\n /**\n * The parent directory as a new `Path`; this instance is unchanged.\n *\n * @see {@link Path.dirname}\n */\n get parent() {\n return this.dirname();\n }\n\n /**\n * The normalized path string stored by this instance.\n */\n get value() {\n return this.#value;\n }\n\n // Public static methods\n\n /**\n * Creates a new path from a Node.js path object.\n *\n * @param pathObject - The path components to format.\n *\n * @returns A new normalized `Path`.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.format}\n */\n static format(pathObject: nodePath.FormatInputPathObject) {\n return new Path(nodePath.format(pathObject));\n }\n\n /**\n * Resolves joined path segments to an absolute path.\n *\n * @remarks\n * Inputs are joined before resolution; unlike Node.js `path.resolve`,\n * later absolute segments do not reset the path.\n *\n * @param paths - The stringified path segments to join.\n *\n * @returns A new absolute `Path`.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.resolve}\n */\n static resolve(...paths: PathLike[]) {\n return new this(...paths).resolve();\n }\n\n // Public instance methods\n [Symbol.for('nodejs.util.inspect.custom')]() {\n return this.#value;\n }\n\n [Symbol.toPrimitive](hint: string) {\n if (hint === 'number') throw new TypeError('Cannot convert a Path to a number');\n return this.#value;\n }\n\n /**\n * Returns the final component of this path.\n *\n * @param suffix - An optional exact suffix to remove.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.basename}\n */\n basename(suffix?: string) {\n return nodePath.basename(this.#value, suffix);\n }\n\n /**\n * Returns the parent directory of this path.\n *\n * @returns A new `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.dirname}\n */\n dirname() {\n return this.#newInstance(nodePath.dirname(this.#value));\n }\n\n /**\n * Returns the extension of the final path component.\n *\n * @returns The extension including its leading dot, or an empty string when absent.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.extname}\n */\n extname() {\n return nodePath.extname(this.#value);\n }\n\n /**\n * Checks whether this path is absolute on the current platform.\n *\n * @returns Whether this path is absolute.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.isAbsolute}\n */\n isAbsolute() {\n return nodePath.isAbsolute(this.#value);\n }\n\n /**\n * Normalizes separators and relative segments in this path.\n *\n * @returns A new `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.normalize}\n */\n normalize() {\n return this.#newInstance(nodePath.normalize(this.#value));\n }\n\n /**\n * Joins additional segments onto this path.\n *\n * @param paths - The stringified path segments to append.\n *\n * @returns A new normalized `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.join}\n */\n join(...paths: PathLike[]) {\n return this.#newInstance(this.#value, ...this.#toStrings(paths));\n }\n\n /**\n * Parses this path into its root, directory, basename, extension, and name.\n *\n * @returns A new object containing the path components.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.parse}\n */\n parse() {\n return nodePath.parse(this.#value);\n }\n\n /**\n * Computes a relative path from this path to the destination.\n *\n * @param to - The destination path.\n *\n * @returns A new `Path`; this instance is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.relative}\n */\n relative(to: PathLike) {\n return this.#newInstance(nodePath.relative(this.#value, to.toString()));\n }\n\n /**\n * Resolves this path to an absolute path using the current working directory.\n *\n * @remarks\n * Relative paths use the current working directory. This instance is unchanged.\n *\n * @returns A new absolute `Path`.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.resolve}\n */\n resolve() {\n return this.#newInstance(nodePath.resolve(this.#value));\n }\n\n toJSON() {\n return this.#value;\n }\n\n /**\n * Converts this path to the Windows namespace form when applicable.\n *\n * @returns The namespace-converted path string; this instance is unchanged.\n * On non-Windows platforms, the path text is unchanged.\n *\n * @see {@link https://nodejs.org/api/path.html | Node.js path.toNamespacedPath}\n */\n toNamespacedPath() {\n return nodePath.toNamespacedPath(this.#value);\n }\n\n /**\n * Returns the stored path as a string.\n */\n toString() {\n return this.#value;\n }\n\n /**\n * Checks access permissions for the stored path.\n *\n * @remarks\n * The promise resolves when the access check succeeds and rejects if access is denied or the path is missing.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.access}\n */\n async access(...args: DropFirstParameters<typeof fsp.access>) {\n return await fsp.access(this.#value, ...args);\n }\n\n /**\n * Appends data to the file at the stored path.\n *\n * @remarks\n * The promise resolves when appending completes; a missing file is created according to the supplied options.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.appendFile}\n */\n async appendFile(...args: DropFirstParameters<typeof fsp.appendFile>) {\n return await fsp.appendFile(this.#value, ...args);\n }\n\n /**\n * Changes the permissions of the stored path.\n *\n * @remarks\n * The promise resolves when the permissions are changed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.chmod}\n */\n async chmod(...args: DropFirstParameters<typeof fsp.chmod>) {\n return await fsp.chmod(this.#value, ...args);\n }\n\n /**\n * Changes the owner and group of the stored path.\n *\n * @remarks\n * The promise resolves when the ownership is changed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.chown}\n */\n async chown(...args: DropFirstParameters<typeof fsp.chown>) {\n return await fsp.chown(this.#value, ...args);\n }\n\n /**\n * Copies the file at the stored path to a destination.\n *\n * @remarks\n * The promise resolves when copying completes. The stored path remains the source path.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.copyFile}\n */\n async copyFile(...args: DropFirstParameters<typeof fsp.copyFile>) {\n return await fsp.copyFile(this.#value, ...args);\n }\n\n /**\n * Creates a directory at the stored path.\n *\n * @remarks\n * The promise resolves after creation. Recursive mode resolves to the first created directory path, or\n * `undefined` if no directory is created; nonrecursive mode resolves without a value.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.mkdir}\n */\n mkdir(options: fs.MakeDirectoryOptions & { recursive: true }): Promise<string | undefined>;\n mkdir(\n options?:\n | (fs.MakeDirectoryOptions & { recursive?: false })\n | fs.Mode\n | null,\n ): Promise<void>;\n mkdir(options?: fs.MakeDirectoryOptions | fs.Mode | null): Promise<string | undefined>;\n async mkdir(...args: any): Promise<any> {\n return await fsp.mkdir(this.#value, ...args);\n }\n\n /**\n * Opens the file at the stored path.\n *\n * @remarks\n * The promise resolves to a new file handle. The caller must close the handle when it is no longer needed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.open}\n */\n async open(...args: DropFirstParameters<typeof fsp.open>) {\n return await fsp.open(this.#value, ...args);\n }\n\n /**\n * Reads the entries of the directory at the stored path.\n *\n * @remarks\n * The promise resolves to names, buffers, or directory entries according to the options.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.readdir}\n */\n readdir(\n options?:\n | BufferEncoding\n | (fs.ObjectEncodingOptions & {\n recursive?: boolean;\n withFileTypes?: false;\n })\n | null,\n ): Promise<string[]>;\n readdir(\n options:\n | 'buffer'\n | {\n encoding: 'buffer';\n recursive?: boolean;\n withFileTypes?: false;\n },\n ): Promise<Buffer[]>;\n readdir(\n options?:\n | BufferEncoding\n | (fs.ObjectEncodingOptions & {\n recursive?: boolean;\n withFileTypes?: false;\n })\n | null,\n ): Promise<Buffer[] | string[]>;\n readdir(\n options: fs.ObjectEncodingOptions & {\n recursive?: boolean;\n withFileTypes: true;\n },\n ): Promise<fs.Dirent[]>;\n readdir(\n options: {\n encoding: 'buffer';\n recursive?: boolean;\n withFileTypes: true;\n },\n ): Promise<Array<fs.Dirent<Buffer>>>;\n async readdir(...args: any): Promise<any> {\n return await fsp.readdir(this.#value, ...args);\n }\n\n /**\n * Reads the entire file at the stored path.\n *\n * @remarks\n * The promise resolves to a string when a text encoding is supplied, or a `Buffer` otherwise.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.readFile}\n */\n readFile(\n options?:\n | (Abortable & {\n encoding?: null;\n flag?: fs.OpenMode;\n })\n | null,\n ): Promise<Buffer>;\n readFile(\n options:\n | (Abortable & {\n encoding: BufferEncoding;\n flag?: fs.OpenMode;\n })\n | BufferEncoding,\n ): Promise<string>;\n readFile(\n options?:\n | (\n & Abortable\n & fs.ObjectEncodingOptions\n & { flag?: fs.OpenMode }\n )\n | BufferEncoding\n | null,\n ): Promise<Buffer | string>;\n async readFile(...args: any): Promise<any> {\n return await fsp.readFile(this.#value, ...args);\n }\n\n /**\n * Renames or moves the file or directory at the stored path.\n *\n * @remarks\n * The promise resolves when the rename completes. This instance retains its original stored path.\n *\n * @param newPath - The destination path; this instance retains its original value.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.rename}\n */\n async rename(newPath: PathLike) {\n return await fsp.rename(this.#value, newPath.toString());\n }\n\n /**\n * Removes the file or directory at the stored path.\n *\n * @remarks\n * The promise resolves after removal according to the supplied options.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.rm}\n */\n async rm(...args: DropFirstParameters<typeof fsp.rm>) {\n return await fsp.rm(this.#value, ...args);\n }\n\n /**\n * Removes the directory at the stored path.\n *\n * @remarks\n * The promise resolves after directory removal.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.rmdir}\n */\n async rmdir(...args: DropFirstParameters<typeof fsp.rmdir>) {\n return await fsp.rmdir(this.#value, ...args);\n }\n\n /**\n * Reads file system metadata for the stored path.\n *\n * @remarks\n * The promise resolves to `Stats`, or `BigIntStats` when `bigint` is enabled.\n * If `throwIfNoEntry` is `false`, a missing path resolves to `undefined` instead of rejecting.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.stat}\n */\n stat(\n opts?: fs.StatOptions & {\n bigint?: false;\n throwIfNoEntry?: true;\n },\n ): Promise<fs.Stats>;\n stat(\n opts: fs.StatOptions & {\n bigint: true;\n throwIfNoEntry?: true;\n },\n ): Promise<fs.BigIntStats>;\n stat(\n opts: fs.StatOptions & {\n bigint?: false;\n throwIfNoEntry: false;\n },\n ): Promise<fs.Stats | undefined>;\n stat(\n opts: fs.StatOptions & {\n bigint: true;\n throwIfNoEntry: false;\n },\n ): Promise<fs.BigIntStats | undefined>;\n stat(opts: fs.StatOptions & { throwIfNoEntry?: true }): Promise<fs.BigIntStats | fs.Stats>;\n stat(opts?: fs.StatOptions): Promise<fs.BigIntStats | fs.Stats | undefined>;\n async stat(opts?: fs.StatOptions): Promise<fs.BigIntStats | fs.Stats | undefined> {\n try {\n return await fsp.stat(this.#value, opts);\n } catch (error) {\n if (\n opts?.throwIfNoEntry === false\n && error instanceof Error\n && 'code' in error\n && error.code === 'ENOENT'\n ) return undefined;\n\n throw error;\n }\n }\n\n /**\n * Truncates or extends the file at the stored path.\n *\n * @remarks\n * The promise resolves when the file reaches the requested length in bytes; the default length is `0`.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.truncate}\n */\n async truncate(...args: DropFirstParameters<typeof fsp.truncate>) {\n return await fsp.truncate(this.#value, ...args);\n }\n\n /**\n * Deletes the file or symbolic link at the stored path.\n *\n * @remarks\n * The promise resolves after deletion; directories are not removed.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.unlink}\n */\n async unlink() {\n return await fsp.unlink(this.#value);\n }\n\n /**\n * Writes data to the file at the stored path.\n *\n * @remarks\n * The promise resolves when writing completes; the default flag replaces existing file contents.\n *\n * @see {@link https://nodejs.org/api/fs.html | Node.js fsPromises.writeFile}\n */\n async writeFile(...args: DropFirstParameters<typeof fsp.writeFile>) {\n return await fsp.writeFile(this.#value, ...args);\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"precision-number.js","names":[],"sources":["../../src/classes/precision-number.ts"],"sourcesContent":["import { Decimal } from 'decimal.js';\n\n/**\n * Values accepted by `PrecisionNumber` constructors and arithmetic methods.\n */\nexport type PrecisionNumberValue = Decimal.Value | PrecisionNumber | { toString: () => string };\n\n/**\n * A fixed-decimal number with mutable and immutable arithmetic operations.\n *\n * @remarks\n * `PrecisionNumber` stores values as `Decimal`, rounds every mutating operation to\n * the configured decimal places, and exposes both mutable and immutable arithmetic\n * methods. Mutable methods (`plus`, `minus`, `times`, `dividedBy`, `modulo`,\n * `pow`, `clamp`, `ceil`, `floor`, `negate`, `absoluteValue`) update the current\n * instance and return `this`; immutable methods prefixed with `to` return a new\n * `PrecisionNumber` with the same precision settings, leaving this instance unchanged.\n * Decimal.js exceptions propagate unchanged; non-finite arithmetic results are not converted into exceptions.\n *\n * @example\n *\n * ```ts\n * import { PrecisionNumber } from '@kikiutils/shared/classes/precision-number';\n *\n * const value = new PrecisionNumber('1.239', 2).plus('2.111');\n * value.toString(); // => '3.35'\n * value.toMinus('1').toString(); // => '2.35' (original value remains '3.35')\n * ```\n */\nexport class PrecisionNumber {\n // Private instance properties\n readonly #decimalPlaces: number;\n readonly #rounding: Decimal.Rounding;\n #decimal: Decimal;\n\n // Constructor\n\n /**\n * Creates a fixed-decimal number.\n *\n * @param value - Initial value (default: `'0'`).\n * @param decimalPlaces - Decimal places retained after operations (default: `2`).\n * @param rounding - Decimal.js rounding mode (default: `Decimal.ROUND_DOWN`).\n */\n constructor(\n value: PrecisionNumberValue = '0',\n decimalPlaces: number = 2,\n rounding: Decimal.Rounding = Decimal.ROUND_DOWN,\n ) {\n this.#decimalPlaces = decimalPlaces;\n this.#rounding = rounding;\n this.#decimal = this.#decimalToFixedDecimal(new Decimal(value.toString().trim()));\n }\n\n // Private instance methods\n #decimalToFixedDecimal(decimal: Decimal) {\n return decimal.toDecimalPlaces(this.#decimalPlaces, this.#rounding);\n }\n\n #valueToString(value: PrecisionNumberValue) {\n return value.toString().trim();\n }\n\n // Public instance accessors\n\n /**\n * Decimal places retained by mutating operations and default formatting.\n */\n get decimalPlaces() {\n return this.#decimalPlaces;\n }\n\n /**\n * Decimal.js rounding mode used by mutating operations and default formatting.\n */\n get rounding() {\n return this.#rounding;\n }\n\n /**\n * Fixed-decimal string using the instance precision and rounding mode.\n */\n get value() {\n return this.#decimal.toFixed(this.#decimalPlaces, this.#rounding);\n }\n\n // Public static methods\n\n /**\n * Formats a value with decimal.js without creating a reusable instance.\n *\n * @param value - Value to format.\n * @param decimalPlaces - Decimal places to output (default: `2`).\n * @param rounding - Decimal.js rounding mode (default: `Decimal.ROUND_DOWN`).\n *\n * @returns Fixed-decimal string.\n */\n static toFixed(\n value: PrecisionNumberValue,\n decimalPlaces: number = 2,\n rounding: Decimal.Rounding = Decimal.ROUND_DOWN,\n ) {\n return new Decimal(value.toString().trim()).toFixed(decimalPlaces, rounding);\n }\n\n // Public instance methods\n\n /**\n * Returns the formatted value when inspected by Node.js utilities.\n */\n [Symbol.for('nodejs.util.inspect.custom')]() {\n return this.value;\n }\n\n /**\n * Converts to a number for numeric coercion and to the fixed string otherwise.\n *\n * @param hint - JavaScript primitive-conversion hint.\n */\n [Symbol.toPrimitive](hint: string) {\n if (hint === 'number') return this.#decimal.toNumber();\n return this.value;\n }\n\n /**\n * Replaces the current value with its absolute value.\n *\n * @returns This instance for chaining.\n */\n absoluteValue() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.absoluteValue());\n return this;\n }\n\n /**\n * Rounds the current value up to the nearest integer in place.\n *\n * @remarks\n * The stored value still uses this instance's configured decimal places for output.\n *\n * @returns This instance for chaining.\n */\n ceil() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.ceil());\n return this;\n }\n\n /**\n * Restricts the current value to the inclusive range `[min, max]` in place.\n *\n * @param min - Lower bound.\n * @param max - Upper bound.\n *\n * @returns This instance for chaining.\n *\n * @throws Error if `min` is greater than `max`.\n */\n clamp(min: PrecisionNumberValue, max: PrecisionNumberValue) {\n const minDecimal = new Decimal(this.#valueToString(min));\n const maxDecimal = new Decimal(this.#valueToString(max));\n if (minDecimal.gt(maxDecimal)) throw new Error('Invalid clamp range: min cannot be greater than max');\n this.#decimal = this.#decimalToFixedDecimal(Decimal.min(Decimal.max(this.#decimal, minDecimal), maxDecimal));\n return this;\n }\n\n /**\n * Returns a new instance with the same value, decimal places, and rounding mode.\n */\n clone() {\n return new PrecisionNumber(this.#decimal, this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Divides the current value by another value in place.\n *\n * @param value - Divisor.\n *\n * @returns This instance for chaining.\n */\n dividedBy(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.dividedBy(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Checks whether the current value equals another value.\n *\n * @param value - Value to compare.\n *\n * @returns Whether both numeric values are equal.\n */\n equals(value: PrecisionNumberValue) {\n return this.#decimal.equals(this.#valueToString(value));\n }\n\n /**\n * Rounds the current value down to the nearest integer in place.\n *\n * @remarks\n * The stored value still uses this instance's configured decimal places for output.\n *\n * @returns This instance for chaining.\n */\n floor() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.floor());\n return this;\n }\n\n /**\n * Checks whether the current value is greater than another value.\n */\n gt(value: PrecisionNumberValue) {\n return this.#decimal.gt(this.#valueToString(value));\n }\n\n /**\n * Checks whether the current value is greater than or equal to another value.\n */\n gte(value: PrecisionNumberValue) {\n return this.#decimal.gte(this.#valueToString(value));\n }\n\n /**\n * Checks whether the current value is finite.\n */\n isFinite() {\n return this.#decimal.isFinite();\n }\n\n /**\n * Checks whether the current value is an integer.\n */\n isInteger() {\n return this.#decimal.isInteger();\n }\n\n /**\n * Checks whether the current value is `NaN`.\n */\n isNaN() {\n return this.#decimal.isNaN();\n }\n\n /**\n * Checks whether the current value is negative.\n */\n isNegative() {\n return this.#decimal.isNegative();\n }\n\n /**\n * Checks whether the current value is positive.\n */\n isPositive() {\n return this.#decimal.isPositive();\n }\n\n /**\n * Checks whether the current value is zero.\n */\n isZero() {\n return this.#decimal.isZero();\n }\n\n /**\n * Checks whether the current value is less than another value.\n */\n lt(value: PrecisionNumberValue) {\n return this.#decimal.lt(this.#valueToString(value));\n }\n\n /**\n * Checks whether the current value is less than or equal to another value.\n */\n lte(value: PrecisionNumberValue) {\n return this.#decimal.lte(this.#valueToString(value));\n }\n\n /**\n * Subtracts another value from the current value in place.\n */\n minus(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.minus(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Replaces the current value with the remainder after division by another value.\n *\n * @param value - Divisor used to compute the remainder.\n *\n * @returns This instance for chaining.\n */\n modulo(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.modulo(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Negates the current value in place.\n */\n negate() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.negated());\n return this;\n }\n\n /**\n * Adds another value to the current value in place.\n */\n plus(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.plus(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Raises the current value to an exponent in place.\n *\n * @param value - Exponent.\n *\n * @returns This instance for chaining.\n */\n pow(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.pow(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Multiplies the current value by another value in place.\n */\n times(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.times(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Returns a new instance with the absolute value.\n */\n toAbsoluteValue() {\n return new PrecisionNumber(this.#decimal.absoluteValue(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance rounded up to the nearest integer.\n */\n toCeil() {\n return new PrecisionNumber(this.#decimal.ceil(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance restricted to the inclusive range `[min, max]`.\n *\n * @param min - Lower bound.\n * @param max - Upper bound.\n *\n * @throws Error if `min` is greater than `max`.\n */\n toClamped(min: PrecisionNumberValue, max: PrecisionNumberValue) {\n return this.clone().clamp(min, max);\n }\n\n /**\n * Returns a new instance divided by another value.\n */\n toDividedBy(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.dividedBy(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n\n /**\n * Returns a new instance rounded down to the nearest integer.\n */\n toFloor() {\n return new PrecisionNumber(this.#decimal.floor(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Serializes to the fixed-decimal string.\n */\n toJSON() {\n return this.value;\n }\n\n /**\n * Returns a new instance with another value subtracted.\n */\n toMinus(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.minus(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n\n /**\n * Returns a new instance with the remainder after division by another value.\n */\n toModulo(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.modulo(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n\n /**\n * Returns a new instance with the value negated.\n */\n toNegated() {\n return new PrecisionNumber(this.#decimal.negated(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance with another value added.\n */\n toPlus(value: PrecisionNumberValue) {\n return new PrecisionNumber(this.#decimal.plus(this.#valueToString(value)), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance raised to an exponent.\n */\n toPow(value: PrecisionNumberValue) {\n return new PrecisionNumber(this.#decimal.pow(this.#valueToString(value)), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Converts the current value to a JavaScript number.\n *\n * @remarks\n * Use `toString`/`value` when preserving decimal precision is more important than native-number ergonomics.\n */\n toNumber() {\n return this.#decimal.toNumber();\n }\n\n /**\n * Converts the current value to its fixed-decimal string.\n */\n toString() {\n return this.value;\n }\n\n /**\n * Formats the current value with custom precision and rounding for this call only.\n */\n toFixed(decimalPlaces: number = this.#decimalPlaces, rounding: Decimal.Rounding = this.#rounding) {\n return this.#decimal.toFixed(decimalPlaces, rounding);\n }\n\n /**\n * Returns a new instance multiplied by another value.\n */\n toTimes(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.times(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"precision-number.js","names":[],"sources":["../../src/classes/precision-number.ts"],"sourcesContent":["import { Decimal } from 'decimal.js';\n\n/**\n * Values accepted by `PrecisionNumber` constructors and arithmetic methods.\n */\nexport type PrecisionNumberValue = Decimal.Value | PrecisionNumber | { toString: () => string };\n\n/**\n * A fixed-decimal number with mutable and immutable arithmetic operations.\n *\n * @remarks\n * `PrecisionNumber` stores values as `Decimal`, rounds every mutating operation to\n * the configured decimal places, and exposes both mutable and immutable arithmetic\n * methods. Mutable methods (`plus`, `minus`, `times`, `dividedBy`, `modulo`,\n * `pow`, `clamp`, `ceil`, `floor`, `negate`, `absoluteValue`) update the current\n * instance and return `this`; immutable methods prefixed with `to` return a new\n * `PrecisionNumber` with the same precision settings, leaving this instance unchanged.\n * Decimal.js exceptions propagate unchanged; non-finite arithmetic results are not converted into exceptions.\n *\n * @example\n *\n * ```ts\n * import { PrecisionNumber } from '@kikiutils/shared/classes/precision-number';\n *\n * const value = new PrecisionNumber('1.239', 2).plus('2.111');\n * value.toString(); // => '3.35'\n * value.toMinus('1').toString(); // => '2.35' (original value remains '3.35')\n * ```\n */\nexport class PrecisionNumber {\n // Private instance properties\n readonly #decimalPlaces: number;\n readonly #rounding: Decimal.Rounding;\n #decimal: Decimal;\n\n // Constructor\n\n /**\n * Creates a fixed-decimal number.\n *\n * @param value - Initial value (default: `'0'`).\n * @param decimalPlaces - Decimal places retained after operations (default: `2`).\n * @param rounding - Decimal.js rounding mode (default: `Decimal.ROUND_DOWN`).\n */\n constructor(\n value: PrecisionNumberValue = '0',\n decimalPlaces: number = 2,\n rounding: Decimal.Rounding = Decimal.ROUND_DOWN,\n ) {\n this.#decimalPlaces = decimalPlaces;\n this.#rounding = rounding;\n this.#decimal = this.#decimalToFixedDecimal(new Decimal(value.toString().trim()));\n }\n\n // Private instance methods\n #decimalToFixedDecimal(decimal: Decimal) {\n return decimal.toDecimalPlaces(this.#decimalPlaces, this.#rounding);\n }\n\n #valueToString(value: PrecisionNumberValue) {\n return value.toString().trim();\n }\n\n // Public instance accessors\n\n /**\n * Decimal places retained by mutating operations and default formatting.\n */\n get decimalPlaces() {\n return this.#decimalPlaces;\n }\n\n /**\n * Decimal.js rounding mode used by mutating operations and default formatting.\n */\n get rounding() {\n return this.#rounding;\n }\n\n /**\n * Fixed-decimal string using the instance precision and rounding mode.\n */\n get value() {\n return this.#decimal.toFixed(this.#decimalPlaces, this.#rounding);\n }\n\n // Public static methods\n\n /**\n * Formats a value with decimal.js without creating a reusable instance.\n *\n * @param value - Value to format.\n * @param decimalPlaces - Decimal places to output (default: `2`).\n * @param rounding - Decimal.js rounding mode (default: `Decimal.ROUND_DOWN`).\n *\n * @returns Fixed-decimal string.\n */\n static toFixed(\n value: PrecisionNumberValue,\n decimalPlaces: number = 2,\n rounding: Decimal.Rounding = Decimal.ROUND_DOWN,\n ) {\n return new Decimal(value.toString().trim()).toFixed(decimalPlaces, rounding);\n }\n\n // Public instance methods\n\n /**\n * Returns the formatted value when inspected by Node.js utilities.\n */\n [Symbol.for('nodejs.util.inspect.custom')]() {\n return this.value;\n }\n\n /**\n * Converts to a number for numeric coercion and to the fixed string otherwise.\n *\n * @param hint - JavaScript primitive-conversion hint.\n */\n [Symbol.toPrimitive](hint: string) {\n if (hint === 'number') return this.#decimal.toNumber();\n return this.value;\n }\n\n /**\n * Replaces the current value with its absolute value.\n *\n * @returns This instance for chaining.\n */\n absoluteValue() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.absoluteValue());\n return this;\n }\n\n /**\n * Rounds the current value up to the nearest integer in place.\n *\n * @remarks\n * The stored value still uses this instance's configured decimal places for output.\n *\n * @returns This instance for chaining.\n */\n ceil() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.ceil());\n return this;\n }\n\n /**\n * Restricts the current value to the inclusive range `[min, max]` in place.\n *\n * @param min - Lower bound.\n * @param max - Upper bound.\n *\n * @returns This instance for chaining.\n *\n * @throws Error if `min` is greater than `max`.\n */\n clamp(min: PrecisionNumberValue, max: PrecisionNumberValue) {\n const minDecimal = new Decimal(this.#valueToString(min));\n const maxDecimal = new Decimal(this.#valueToString(max));\n if (minDecimal.gt(maxDecimal)) throw new Error('Invalid clamp range: min cannot be greater than max');\n this.#decimal = this.#decimalToFixedDecimal(Decimal.min(Decimal.max(this.#decimal, minDecimal), maxDecimal));\n return this;\n }\n\n /**\n * Returns a new instance with the same value, decimal places, and rounding mode.\n */\n clone() {\n return new PrecisionNumber(this.#decimal, this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Divides the current value by another value in place.\n *\n * @param value - Divisor.\n *\n * @returns This instance for chaining.\n */\n dividedBy(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.dividedBy(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Checks whether the current value equals another value.\n *\n * @param value - Value to compare.\n *\n * @returns Whether both numeric values are equal.\n */\n equals(value: PrecisionNumberValue) {\n return this.#decimal.equals(this.#valueToString(value));\n }\n\n /**\n * Rounds the current value down to the nearest integer in place.\n *\n * @remarks\n * The stored value still uses this instance's configured decimal places for output.\n *\n * @returns This instance for chaining.\n */\n floor() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.floor());\n return this;\n }\n\n /**\n * Checks whether the current value is greater than another value.\n */\n gt(value: PrecisionNumberValue) {\n return this.#decimal.gt(this.#valueToString(value));\n }\n\n /**\n * Checks whether the current value is greater than or equal to another value.\n */\n gte(value: PrecisionNumberValue) {\n return this.#decimal.gte(this.#valueToString(value));\n }\n\n /**\n * Checks whether the current value is finite.\n */\n isFinite() {\n return this.#decimal.isFinite();\n }\n\n /**\n * Checks whether the current value is an integer.\n */\n isInteger() {\n return this.#decimal.isInteger();\n }\n\n /**\n * Checks whether the current value is `NaN`.\n */\n isNaN() {\n return this.#decimal.isNaN();\n }\n\n /**\n * Checks whether the current value is negative.\n */\n isNegative() {\n return this.#decimal.isNegative();\n }\n\n /**\n * Checks whether the current value is positive.\n */\n isPositive() {\n return this.#decimal.isPositive();\n }\n\n /**\n * Checks whether the current value is zero.\n */\n isZero() {\n return this.#decimal.isZero();\n }\n\n /**\n * Checks whether the current value is less than another value.\n */\n lt(value: PrecisionNumberValue) {\n return this.#decimal.lt(this.#valueToString(value));\n }\n\n /**\n * Checks whether the current value is less than or equal to another value.\n */\n lte(value: PrecisionNumberValue) {\n return this.#decimal.lte(this.#valueToString(value));\n }\n\n /**\n * Subtracts another value from the current value in place.\n */\n minus(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.minus(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Replaces the current value with the remainder after division by another value.\n *\n * @param value - Divisor used to compute the remainder.\n *\n * @returns This instance for chaining.\n */\n modulo(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.modulo(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Negates the current value in place.\n */\n negate() {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.negated());\n return this;\n }\n\n /**\n * Adds another value to the current value in place.\n */\n plus(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.plus(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Raises the current value to an exponent in place.\n *\n * @param value - Exponent.\n *\n * @returns This instance for chaining.\n */\n pow(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.pow(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Multiplies the current value by another value in place.\n */\n times(value: PrecisionNumberValue) {\n this.#decimal = this.#decimalToFixedDecimal(this.#decimal.times(this.#valueToString(value)));\n return this;\n }\n\n /**\n * Returns a new instance with the absolute value.\n */\n toAbsoluteValue() {\n return new PrecisionNumber(this.#decimal.absoluteValue(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance rounded up to the nearest integer.\n */\n toCeil() {\n return new PrecisionNumber(this.#decimal.ceil(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance restricted to the inclusive range `[min, max]`.\n *\n * @param min - Lower bound.\n * @param max - Upper bound.\n *\n * @throws Error if `min` is greater than `max`.\n */\n toClamped(min: PrecisionNumberValue, max: PrecisionNumberValue) {\n return this.clone().clamp(min, max);\n }\n\n /**\n * Returns a new instance divided by another value.\n */\n toDividedBy(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.dividedBy(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n\n /**\n * Returns a new instance rounded down to the nearest integer.\n */\n toFloor() {\n return new PrecisionNumber(this.#decimal.floor(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Serializes to the fixed-decimal string.\n */\n toJSON() {\n return this.value;\n }\n\n /**\n * Returns a new instance with another value subtracted.\n */\n toMinus(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.minus(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n\n /**\n * Returns a new instance with the remainder after division by another value.\n */\n toModulo(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.modulo(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n\n /**\n * Returns a new instance with the value negated.\n */\n toNegated() {\n return new PrecisionNumber(this.#decimal.negated(), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance with another value added.\n */\n toPlus(value: PrecisionNumberValue) {\n return new PrecisionNumber(this.#decimal.plus(this.#valueToString(value)), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Returns a new instance raised to an exponent.\n */\n toPow(value: PrecisionNumberValue) {\n return new PrecisionNumber(this.#decimal.pow(this.#valueToString(value)), this.#decimalPlaces, this.#rounding);\n }\n\n /**\n * Converts the current value to a JavaScript number.\n *\n * @remarks\n * Use `toString`/`value` when preserving decimal precision is more important than native-number ergonomics.\n */\n toNumber() {\n return this.#decimal.toNumber();\n }\n\n /**\n * Converts the current value to its fixed-decimal string.\n */\n toString() {\n return this.value;\n }\n\n /**\n * Formats the current value with custom precision and rounding for this call only.\n */\n toFixed(decimalPlaces: number = this.#decimalPlaces, rounding: Decimal.Rounding = this.#rounding) {\n return this.#decimal.toFixed(decimalPlaces, rounding);\n }\n\n /**\n * Returns a new instance multiplied by another value.\n */\n toTimes(value: PrecisionNumberValue) {\n return new PrecisionNumber(\n this.#decimal.times(this.#valueToString(value)),\n this.#decimalPlaces,\n this.#rounding,\n );\n }\n}\n"],"mappings":"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"}
package/dist/env.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"env.js","names":[],"sources":["../src/env.ts"],"sourcesContent":["/**\n * Custom error class for handling missing environment variables.\n *\n * @remarks\n * Extends the built-in `Error` class and includes the missing key.\n */\nexport class EnvironmentNotFoundError extends Error {\n readonly key: string;\n\n /**\n * Creates an error identifying a missing environment variable.\n *\n * @param key - The missing environment variable key.\n */\n constructor(key: string) {\n super(`Missing environment variable: ${key}`);\n this.key = key;\n this.name = this.constructor.name;\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\n/**\n * Retrieves the value of an environment variable, or throws an error if it is not defined.\n *\n * @remarks\n * Only checks for `process.env[key] === undefined`. An empty string (for example `''`) or any falsy string\n * value like `'0'` or `'false'` is considered a valid (defined) value.\n *\n * @param key - The environment variable key to retrieve.\n *\n * @returns The value of the environment variable.\n *\n * @throws EnvironmentNotFoundError if the environment variable is not defined.\n *\n * @example\n *\n * ```ts\n * import {\n * checkAndGetEnvValue,\n * EnvironmentNotFoundError,\n * } from '@kikiutils/shared/env';\n *\n * process.env.API_KEY = '';\n * checkAndGetEnvValue('API_KEY'); // => ''\n *\n * delete process.env.API_KEY;\n * try {\n * checkAndGetEnvValue('API_KEY');\n * } catch (error) {\n * console.log(error instanceof EnvironmentNotFoundError); // => true\n * }\n * ```\n */\nexport function checkAndGetEnvValue(key: string) {\n if (process.env[key] === undefined) throw new EnvironmentNotFoundError(key);\n return process.env[key];\n}\n"],"mappings":";;;;;;;AAMA,IAAa,2BAAb,cAA8C,MAAM;CAChD;;;;;;CAOA,YAAY,KAAa;EACrB,MAAM,iCAAiC,KAAK;EAC5C,KAAK,MAAM;EACX,KAAK,OAAO,KAAK,YAAY;EAC7B,MAAM,oBAAoB,MAAM,KAAK,WAAW;CACpD;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,oBAAoB,KAAa;CAC7C,IAAI,QAAQ,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,yBAAyB,GAAG;CAC1E,OAAO,QAAQ,IAAI;AACvB"}
1
+ {"version":3,"file":"env.js","names":[],"sources":["../src/env.ts"],"sourcesContent":["/**\n * Custom error class for handling missing environment variables.\n *\n * @remarks\n * Extends the built-in `Error` class and includes the missing key.\n */\nexport class EnvironmentNotFoundError extends Error {\n readonly key: string;\n\n /**\n * Creates an error identifying a missing environment variable.\n *\n * @param key - The missing environment variable key.\n */\n constructor(key: string) {\n super(`Missing environment variable: ${key}`);\n this.key = key;\n this.name = this.constructor.name;\n Error.captureStackTrace?.(this, this.constructor);\n }\n}\n\n/**\n * Retrieves the value of an environment variable, or throws an error if it is not defined.\n *\n * @remarks\n * Only checks for `process.env[key] === undefined`. An empty string (for example `''`) or any falsy string\n * value like `'0'` or `'false'` is considered a valid (defined) value.\n *\n * @param key - The environment variable key to retrieve.\n *\n * @returns The value of the environment variable.\n *\n * @throws EnvironmentNotFoundError if the environment variable is not defined.\n *\n * @example\n *\n * ```ts\n * import {\n * checkAndGetEnvValue,\n * EnvironmentNotFoundError,\n * } from '@kikiutils/shared/env';\n *\n * process.env.API_KEY = '';\n * checkAndGetEnvValue('API_KEY'); // => ''\n *\n * delete process.env.API_KEY;\n * try {\n * checkAndGetEnvValue('API_KEY');\n * } catch (error) {\n * console.log(error instanceof EnvironmentNotFoundError); // => true\n * }\n * ```\n */\nexport function checkAndGetEnvValue(key: string) {\n if (process.env[key] === undefined) throw new EnvironmentNotFoundError(key);\n return process.env[key];\n}\n"],"mappings":";;;;;;;AAMA,IAAa,2BAAb,cAA8C,MAAM;CACvC;;;;;;CAOT,YAAY,KAAa;EACrB,MAAM,iCAAiC,KAAK;EAC5C,KAAK,MAAM;EACX,KAAK,OAAO,KAAK,YAAY;EAC7B,MAAM,oBAAoB,MAAM,KAAK,WAAW;CACpD;AACJ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,oBAAoB,KAAa;CAC7C,IAAI,QAAQ,IAAI,SAAS,KAAA,GAAW,MAAM,IAAI,yBAAyB,GAAG;CAC1E,OAAO,QAAQ,IAAI;AACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","names":[],"sources":["../../../src/logger/_internals/logger.ts"],"sourcesContent":["import {\n objectHasOwn,\n objectKeys,\n} from 'ts-extras';\n\nimport type {\n Logger,\n LoggerDiagnostic,\n LoggerLevel,\n LoggerMethod,\n LoggerOptions,\n} from '../types';\n\nimport { loggerMethods } from './levels';\nimport type { LoggerRecord } from './types';\n\ninterface LoggerHooks {\n /**\n * A non-throwing reporter created by createDiagnosticReporter.\n */\n report: (event: LoggerDiagnostic) => void;\n write: (record: LoggerRecord) => Promise<void> | void;\n}\n\n// Constants/Variables\nconst booleanPresentationKeys = [\n 'console',\n 'icons',\n 'timestamp',\n] as const;\n\nconst stringPresentationKeys = [\n 'prefix',\n 'suffix',\n] as const;\n\n// Functions\n\n/**\n * A non-recursive, rate-limited diagnostic boundary shared by server and browser adapters.\n */\nexport function createDiagnosticReporter(\n onError?: (event: Readonly<LoggerDiagnostic>) => void,\n fallback?: (message: string) => void,\n): (event: LoggerDiagnostic) => void {\n let reporting = false;\n let callbackPending = false;\n const stateByType = new Map<LoggerDiagnostic['type'], { lastReportedAt: number; suppressedCount: number }>();\n\n return (event) => {\n if (reporting) return;\n try {\n const now = Date.now();\n const state = stateByType.get(event.type);\n if (state && now - state.lastReportedAt < 1000) {\n state.suppressedCount++;\n return;\n }\n\n reporting = true;\n const suppressedCount = state?.suppressedCount ?? 0;\n if (suppressedCount) {\n event = {\n ...event,\n suppressedCount,\n };\n }\n\n stateByType.set(\n event.type,\n {\n lastReportedAt: now,\n suppressedCount: 0,\n },\n );\n\n try {\n const summary = suppressedCount ? ` (${suppressedCount} similar diagnostics suppressed)` : '';\n fallback?.(`[logger] ${event.message}${summary}\\n`);\n } catch {\n // Diagnostics must never re-enter the normal logging pipeline.\n }\n\n if (!callbackPending) {\n try {\n callbackPending = true;\n const result: unknown = onError?.(Object.freeze({ ...event }));\n if (result !== null && (typeof result === 'object' || typeof result === 'function')) {\n void Promise.resolve(result).then(\n () => {\n callbackPending = false;\n },\n () => {\n callbackPending = false;\n },\n );\n } else {\n callbackPending = false;\n }\n } catch {\n callbackPending = false;\n // User diagnostic callbacks have the same non-throwing boundary.\n }\n }\n } catch { /* Diagnostics must never escape into the logging pipeline. */ } finally {\n reporting = false;\n }\n };\n}\n\nexport function createLoggerCore(options: LoggerOptions, hooks: LoggerHooks): Readonly<Logger> {\n validateLevel(options.level);\n validatePresentation(options);\n if (options.onError !== undefined && typeof options.onError !== 'function') {\n throw new TypeError('Logger onError must be a function');\n }\n\n const presentation = {\n console: options.console ?? true,\n icons: options.icons ?? true,\n prefix: options.prefix ?? '',\n suffix: options.suffix ?? '',\n timestamp: options.timestamp ?? true,\n };\n\n const { report } = hooks;\n\n let override = options.level === 'env' ? undefined : options.level;\n let previousEnvironment: string | undefined;\n let currentLevel: LoggerLevel = override ?? 'info';\n let environmentRead = false;\n\n function refreshLevel() {\n if (override !== undefined) return;\n const input = readEnvironmentLevel();\n if (environmentRead && input === previousEnvironment) return;\n environmentRead = true;\n previousEnvironment = input;\n const normalized = input?.trim().toLowerCase() || undefined;\n currentLevel = isLevel(normalized) ? normalized : 'info';\n if (normalized !== undefined && !isLevel(normalized)) {\n report({\n message: 'Invalid LOG_LEVEL; using info',\n type: 'invalid-level',\n });\n }\n }\n\n const logger = {\n configure(configuration) {\n validatePresentation(configuration);\n for (const name of booleanPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n\n for (const name of stringPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n },\n isLevelEnabled(level) {\n if (level !== 'success' && !isLevel(level)) throw new TypeError('Invalid logger level');\n refreshLevel();\n return level !== 'silent' && getLevelSeverity(level) >= getLevelSeverity(currentLevel);\n },\n setLevel(level) {\n validateLevel(level);\n if (level === undefined) throw new TypeError('Invalid logger level');\n override = level === 'env' ? undefined : level;\n environmentRead = false;\n if (override !== undefined) {\n currentLevel = override;\n } else {\n refreshLevel();\n }\n },\n } as Logger;\n\n for (const type of objectKeys(loggerMethods) as LoggerMethod[]) {\n logger[type] = async (...args: unknown[]) => {\n try {\n refreshLevel();\n if (loggerMethods[type].severity < getLevelSeverity(currentLevel)) return;\n await hooks.write({\n ...presentation,\n args,\n date: new Date(),\n type,\n });\n } catch (error) {\n report({\n error,\n message: 'Logger output failed',\n type: 'format',\n });\n }\n };\n }\n\n return Object.freeze(logger);\n}\n\nfunction getLevelSeverity(level: LoggerLevel | LoggerMethod): number {\n return level === 'silent' ? Number.POSITIVE_INFINITY : loggerMethods[level].severity;\n}\n\nfunction isLevel(value: unknown): value is LoggerLevel {\n return typeof value === 'string'\n && (value === 'silent' || (value !== 'success' && objectHasOwn(loggerMethods, value)));\n}\n\nfunction readEnvironmentLevel(): string | undefined {\n return (globalThis as { process?: { env?: { LOG_LEVEL?: string } } }).process?.env?.LOG_LEVEL;\n}\n\nfunction validateLevel(value: unknown): asserts value is 'env' | LoggerLevel | undefined {\n if (value !== undefined && value !== 'env' && !isLevel(value)) throw new TypeError('Invalid logger level');\n}\n\nfunction validatePresentation(options: Pick<LoggerOptions, 'console' | 'icons' | 'prefix' | 'suffix' | 'timestamp'>) {\n for (const name of booleanPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'boolean') {\n throw new TypeError(`Logger ${name} must be a boolean`);\n }\n }\n\n for (const name of stringPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'string') {\n throw new TypeError(`Logger ${name} must be a string`);\n }\n }\n}\n"],"mappings":";;;AAyBA,MAAM,0BAA0B;CAC5B;CACA;CACA;AACJ;AAEA,MAAM,yBAAyB,CAC3B,UACA,QACJ;;;;AAOA,SAAgB,yBACZ,SACA,UACiC;CACjC,IAAI,YAAY;CAChB,IAAI,kBAAkB;CACtB,MAAM,8BAAc,IAAI,IAAmF;CAE3G,QAAQ,UAAU;EACd,IAAI,WAAW;EACf,IAAI;GACA,MAAM,MAAM,KAAK,IAAI;GACrB,MAAM,QAAQ,YAAY,IAAI,MAAM,IAAI;GACxC,IAAI,SAAS,MAAM,MAAM,iBAAiB,KAAM;IAC5C,MAAM;IACN;GACJ;GAEA,YAAY;GACZ,MAAM,kBAAkB,OAAO,mBAAmB;GAClD,IAAI,iBACA,QAAQ;IACJ,GAAG;IACH;GACJ;GAGJ,YAAY,IACR,MAAM,MACN;IACI,gBAAgB;IAChB,iBAAiB;GACrB,CACJ;GAEA,IAAI;IACA,MAAM,UAAU,kBAAkB,KAAK,gBAAgB,oCAAoC;IAC3F,WAAW,YAAY,MAAM,UAAU,QAAQ,GAAG;GACtD,QAAQ,CAER;GAEA,IAAI,CAAC,iBACD,IAAI;IACA,kBAAkB;IAClB,MAAM,SAAkB,UAAU,OAAO,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC;IAC7D,IAAI,WAAW,SAAS,OAAO,WAAW,YAAY,OAAO,WAAW,aACpE,QAAa,QAAQ,MAAM,CAAC,CAAC,WACnB;KACF,kBAAkB;IACtB,SACM;KACF,kBAAkB;IACtB,CACJ;SAEA,kBAAkB;GAE1B,QAAQ;IACJ,kBAAkB;GAEtB;EAER,QAAQ,CAAiE,UAAU;GAC/E,YAAY;EAChB;CACJ;AACJ;AAEA,SAAgB,iBAAiB,SAAwB,OAAsC;CAC3F,cAAc,QAAQ,KAAK;CAC3B,qBAAqB,OAAO;CAC5B,IAAI,QAAQ,YAAY,KAAA,KAAa,OAAO,QAAQ,YAAY,YAC5D,MAAM,IAAI,UAAU,mCAAmC;CAG3D,MAAM,eAAe;EACjB,SAAS,QAAQ,WAAW;EAC5B,OAAO,QAAQ,SAAS;EACxB,QAAQ,QAAQ,UAAU;EAC1B,QAAQ,QAAQ,UAAU;EAC1B,WAAW,QAAQ,aAAa;CACpC;CAEA,MAAM,EAAE,WAAW;CAEnB,IAAI,WAAW,QAAQ,UAAU,QAAQ,KAAA,IAAY,QAAQ;CAC7D,IAAI;CACJ,IAAI,eAA4B,YAAY;CAC5C,IAAI,kBAAkB;CAEtB,SAAS,eAAe;EACpB,IAAI,aAAa,KAAA,GAAW;EAC5B,MAAM,QAAQ,qBAAqB;EACnC,IAAI,mBAAmB,UAAU,qBAAqB;EACtD,kBAAkB;EAClB,sBAAsB;EACtB,MAAM,aAAa,OAAO,KAAK,CAAC,CAAC,YAAY,KAAK,KAAA;EAClD,eAAe,QAAQ,UAAU,IAAI,aAAa;EAClD,IAAI,eAAe,KAAA,KAAa,CAAC,QAAQ,UAAU,GAC/C,OAAO;GACH,SAAS;GACT,MAAM;EACV,CAAC;CAET;CAEA,MAAM,SAAS;EACX,UAAU,eAAe;GACrB,qBAAqB,aAAa;GAClC,KAAK,MAAM,QAAQ,yBAAyB;IACxC,MAAM,QAAQ,cAAc;IAC5B,IAAI,UAAU,KAAA,GAAW,aAAa,QAAQ;GAClD;GAEA,KAAK,MAAM,QAAQ,wBAAwB;IACvC,MAAM,QAAQ,cAAc;IAC5B,IAAI,UAAU,KAAA,GAAW,aAAa,QAAQ;GAClD;EACJ;EACA,eAAe,OAAO;GAClB,IAAI,UAAU,aAAa,CAAC,QAAQ,KAAK,GAAG,MAAM,IAAI,UAAU,sBAAsB;GACtF,aAAa;GACb,OAAO,UAAU,YAAY,iBAAiB,KAAK,KAAK,iBAAiB,YAAY;EACzF;EACA,SAAS,OAAO;GACZ,cAAc,KAAK;GACnB,IAAI,UAAU,KAAA,GAAW,MAAM,IAAI,UAAU,sBAAsB;GACnE,WAAW,UAAU,QAAQ,KAAA,IAAY;GACzC,kBAAkB;GAClB,IAAI,aAAa,KAAA,GACb,eAAe;QAEf,aAAa;EAErB;CACJ;CAEA,KAAK,MAAM,QAAQ,WAAW,aAAa,GACvC,OAAO,QAAQ,OAAO,GAAG,SAAoB;EACzC,IAAI;GACA,aAAa;GACb,IAAI,cAAc,KAAK,CAAC,WAAW,iBAAiB,YAAY,GAAG;GACnE,MAAM,MAAM,MAAM;IACd,GAAG;IACH;IACA,sBAAM,IAAI,KAAK;IACf;GACJ,CAAC;EACL,SAAS,OAAO;GACZ,OAAO;IACH;IACA,SAAS;IACT,MAAM;GACV,CAAC;EACL;CACJ;CAGJ,OAAO,OAAO,OAAO,MAAM;AAC/B;AAEA,SAAS,iBAAiB,OAA2C;CACjE,OAAO,UAAU,WAAW,OAAO,oBAAoB,cAAc,MAAM,CAAC;AAChF;AAEA,SAAS,QAAQ,OAAsC;CACnD,OAAO,OAAO,UAAU,aAClB,UAAU,YAAa,UAAU,aAAa,aAAa,eAAe,KAAK;AACzF;AAEA,SAAS,uBAA2C;CAChD,OAAQ,WAA8D,SAAS,KAAK;AACxF;AAEA,SAAS,cAAc,OAAkE;CACrF,IAAI,UAAU,KAAA,KAAa,UAAU,SAAS,CAAC,QAAQ,KAAK,GAAG,MAAM,IAAI,UAAU,sBAAsB;AAC7G;AAEA,SAAS,qBAAqB,SAAuF;CACjH,KAAK,MAAM,QAAQ,yBACf,IAAI,QAAQ,UAAU,KAAA,KAAa,OAAO,QAAQ,UAAU,WACxD,MAAM,IAAI,UAAU,UAAU,KAAK,mBAAmB;CAI9D,KAAK,MAAM,QAAQ,wBACf,IAAI,QAAQ,UAAU,KAAA,KAAa,OAAO,QAAQ,UAAU,UACxD,MAAM,IAAI,UAAU,UAAU,KAAK,kBAAkB;AAGjE"}
1
+ {"version":3,"file":"logger.js","names":[],"sources":["../../../src/logger/_internals/logger.ts"],"sourcesContent":["import {\n objectHasOwn,\n objectKeys,\n} from 'ts-extras';\n\nimport type {\n Logger,\n LoggerDiagnostic,\n LoggerLevel,\n LoggerMethod,\n LoggerOptions,\n} from '../types';\n\nimport { loggerMethods } from './levels';\nimport type { LoggerRecord } from './types';\n\ninterface LoggerHooks {\n /**\n * A non-throwing reporter created by createDiagnosticReporter.\n */\n report: (event: LoggerDiagnostic) => void;\n write: (record: LoggerRecord) => Promise<void> | void;\n}\n\n// Constants/Variables\nconst booleanPresentationKeys = [\n 'console',\n 'icons',\n 'timestamp',\n] as const;\n\nconst stringPresentationKeys = [\n 'prefix',\n 'suffix',\n] as const;\n\n// Functions\n\n/**\n * A non-recursive, rate-limited diagnostic boundary shared by server and browser adapters.\n */\nexport function createDiagnosticReporter(\n onError?: (event: Readonly<LoggerDiagnostic>) => void,\n fallback?: (message: string) => void,\n): (event: LoggerDiagnostic) => void {\n let reporting = false;\n let callbackPending = false;\n const stateByType = new Map<LoggerDiagnostic['type'], { lastReportedAt: number; suppressedCount: number }>();\n\n return (event) => {\n if (reporting) return;\n try {\n const now = Date.now();\n const state = stateByType.get(event.type);\n if (state && now - state.lastReportedAt < 1000) {\n state.suppressedCount++;\n return;\n }\n\n reporting = true;\n const suppressedCount = state?.suppressedCount ?? 0;\n if (suppressedCount) {\n event = {\n ...event,\n suppressedCount,\n };\n }\n\n stateByType.set(\n event.type,\n {\n lastReportedAt: now,\n suppressedCount: 0,\n },\n );\n\n try {\n const summary = suppressedCount ? ` (${suppressedCount} similar diagnostics suppressed)` : '';\n fallback?.(`[logger] ${event.message}${summary}\\n`);\n } catch {\n // Diagnostics must never re-enter the normal logging pipeline.\n }\n\n if (!callbackPending) {\n try {\n callbackPending = true;\n const result: unknown = onError?.(Object.freeze({ ...event }));\n if (result !== null && (typeof result === 'object' || typeof result === 'function')) {\n void Promise.resolve(result).then(\n () => {\n callbackPending = false;\n },\n () => {\n callbackPending = false;\n },\n );\n } else {\n callbackPending = false;\n }\n } catch {\n callbackPending = false;\n // User diagnostic callbacks have the same non-throwing boundary.\n }\n }\n } catch { /* Diagnostics must never escape into the logging pipeline. */ } finally {\n reporting = false;\n }\n };\n}\n\nexport function createLoggerCore(options: LoggerOptions, hooks: LoggerHooks): Readonly<Logger> {\n validateLevel(options.level);\n validatePresentation(options);\n if (options.onError !== undefined && typeof options.onError !== 'function') {\n throw new TypeError('Logger onError must be a function');\n }\n\n const presentation = {\n console: options.console ?? true,\n icons: options.icons ?? true,\n prefix: options.prefix ?? '',\n suffix: options.suffix ?? '',\n timestamp: options.timestamp ?? true,\n };\n\n const { report } = hooks;\n\n let override = options.level === 'env' ? undefined : options.level;\n let previousEnvironment: string | undefined;\n let currentLevel: LoggerLevel = override ?? 'info';\n let environmentRead = false;\n\n function refreshLevel() {\n if (override !== undefined) return;\n const input = readEnvironmentLevel();\n if (environmentRead && input === previousEnvironment) return;\n environmentRead = true;\n previousEnvironment = input;\n const normalized = input?.trim().toLowerCase() || undefined;\n currentLevel = isLevel(normalized) ? normalized : 'info';\n if (normalized !== undefined && !isLevel(normalized)) {\n report({\n message: 'Invalid LOG_LEVEL; using info',\n type: 'invalid-level',\n });\n }\n }\n\n const logger = {\n configure(configuration) {\n validatePresentation(configuration);\n for (const name of booleanPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n\n for (const name of stringPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n },\n isLevelEnabled(level) {\n if (level !== 'success' && !isLevel(level)) throw new TypeError('Invalid logger level');\n refreshLevel();\n return level !== 'silent' && getLevelSeverity(level) >= getLevelSeverity(currentLevel);\n },\n setLevel(level) {\n validateLevel(level);\n if (level === undefined) throw new TypeError('Invalid logger level');\n override = level === 'env' ? undefined : level;\n environmentRead = false;\n if (override !== undefined) {\n currentLevel = override;\n } else {\n refreshLevel();\n }\n },\n } as Logger;\n\n for (const type of objectKeys(loggerMethods) as LoggerMethod[]) {\n logger[type] = async (...args: unknown[]) => {\n try {\n refreshLevel();\n if (loggerMethods[type].severity < getLevelSeverity(currentLevel)) return;\n await hooks.write({\n ...presentation,\n args,\n date: new Date(),\n type,\n });\n } catch (error) {\n report({\n error,\n message: 'Logger output failed',\n type: 'format',\n });\n }\n };\n }\n\n return Object.freeze(logger);\n}\n\nfunction getLevelSeverity(level: LoggerLevel | LoggerMethod): number {\n return level === 'silent' ? Number.POSITIVE_INFINITY : loggerMethods[level].severity;\n}\n\nfunction isLevel(value: unknown): value is LoggerLevel {\n return typeof value === 'string'\n && (value === 'silent' || (value !== 'success' && objectHasOwn(loggerMethods, value)));\n}\n\nfunction readEnvironmentLevel(): string | undefined {\n return (globalThis as { process?: { env?: { LOG_LEVEL?: string } } }).process?.env?.LOG_LEVEL;\n}\n\nfunction validateLevel(value: unknown): asserts value is 'env' | LoggerLevel | undefined {\n if (value !== undefined && value !== 'env' && !isLevel(value)) throw new TypeError('Invalid logger level');\n}\n\nfunction validatePresentation(options: Pick<LoggerOptions, 'console' | 'icons' | 'prefix' | 'suffix' | 'timestamp'>) {\n for (const name of booleanPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'boolean') {\n throw new TypeError(`Logger ${name} must be a boolean`);\n }\n }\n\n for (const name of stringPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'string') {\n throw new TypeError(`Logger ${name} must be a string`);\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/logger/node/index.ts"],"sourcesContent":["import { writeSync } from 'node:fs';\nimport { stripVTControlCharacters } from 'node:util';\n\nimport { supportsLoggerColor } from '../_internals/formatter';\nimport { getLoggerStreamName } from '../_internals/levels';\nimport {\n createDiagnosticReporter,\n createLoggerCore,\n} from '../_internals/logger';\nimport type { LoggerDiagnostic } from '../types';\n\nimport { createFileWriter } from './_internals/file-writer';\nimport { formatNodeLoggerRecord } from './_internals/formatter';\nimport type {\n NodeLogger,\n NodeLoggerOptions,\n} from './types';\n\ninterface ConsoleStreamState {\n onClose: () => void;\n onError: (error: Error) => void;\n owners: Set<object>;\n pending: Set<(error?: Error | null) => void>;\n}\n\n// Constants/Variables\nconst consoleStreams = new WeakMap<NodeJS.WriteStream, ConsoleStreamState>();\n\n// Functions\n\n/**\n * Creates a Node.js/Bun logger with optional bounded, append-only file output and rotation.\n *\n * Log calls never throw or reject. Awaiting them waits for processing, not proof of successful\n * persistence: overload drops the newest file record and failures are reported safely. Factory\n * validation/open failures reject. Call close explicitly; no process signal handlers are installed.\n */\nexport async function createNodeLogger(options: NodeLoggerOptions = {}): Promise<Readonly<NodeLogger>> {\n const report = createDiagnosticReporter(options.onError, (message) => writeSync(2, message));\n let writer: Awaited<ReturnType<typeof createFileWriter>> | undefined;\n let closed = false;\n let closePromise: Promise<void> | undefined;\n const consoleOwner = {};\n const consoleTargets = new Set<NodeJS.WriteStream>();\n const consoleWrites = new Set<Promise<void>>();\n const logger = createLoggerCore(\n options,\n {\n report,\n async write(record) {\n if (closed) {\n report({\n message: 'Logger is closed.',\n type: 'closed',\n });\n\n return;\n }\n\n if (!record.console && !writer) return;\n const target = process[getLoggerStreamName(record.type)];\n\n const text = formatNodeLoggerRecord(record, record.console && supportsLoggerColor(target));\n if (record.console) {\n const line = `${text}\\n`;\n consoleTargets.add(target);\n const completion = writeConsole(target, line, report, consoleOwner);\n consoleWrites.add(completion);\n void completion.then(() => consoleWrites.delete(completion));\n const fileCompletion = writer?.write(`${stripVTControlCharacters(text)}\\n`);\n return fileCompletion\n ? await Promise.all([\n completion,\n fileCompletion,\n ]).then(() => {})\n : await completion;\n }\n\n return await writer?.write(`${stripVTControlCharacters(text)}\\n`);\n },\n },\n );\n\n if (options.file !== undefined && options.file !== false) writer = await createFileWriter(options.file, report);\n\n return Object.freeze({\n ...logger,\n async close() {\n if (closePromise) return await closePromise;\n closed = true;\n closePromise = (async () => {\n await Promise.all([\n writer?.close(),\n ...consoleWrites,\n ]);\n\n for (const target of consoleTargets) {\n const state = consoleStreams.get(target);\n if (!state) continue;\n state.owners.delete(consoleOwner);\n if (!state.owners.size) {\n target.off('error', state.onError);\n target.off('close', state.onClose);\n consoleStreams.delete(target);\n }\n }\n\n consoleTargets.clear();\n })();\n\n return await closePromise;\n },\n getStats: () => writer?.getStats() ?? Object.freeze({\n droppedCount: 0,\n pendingBytes: 0,\n writeErrorCount: 0,\n }),\n });\n}\n\n/**\n * One error listener per shared output stream; retained until its last owning logger calls close().\n */\nasync function writeConsole(\n target: NodeJS.WriteStream,\n text: string,\n report: (event: LoggerDiagnostic) => void,\n owner: object,\n) {\n let state = consoleStreams.get(target);\n if (!state) {\n const pending = new Set<(error?: Error | null) => void>();\n const onError = (error: Error) => {\n for (const settle of pending) settle(error);\n };\n\n const onClose = () => onError(new Error('Console stream closed.'));\n state = {\n onClose,\n onError,\n owners: new Set(),\n pending,\n };\n\n consoleStreams.set(target, state);\n target.on('error', onError);\n target.on('close', onClose);\n }\n\n state.owners.add(owner);\n const current = state;\n return await new Promise<void>((resolveWritten) => {\n let settled = false;\n const settle = (error?: Error | null) => {\n if (settled) return;\n settled = true;\n current.pending.delete(settle);\n if (error) {\n report({\n error,\n message: 'Console output failed.',\n type: 'console',\n });\n }\n\n resolveWritten();\n };\n\n current.pending.add(settle);\n try {\n target.write(text, settle);\n } catch (error) {\n settle(error instanceof Error ? error : new Error('Console output failed.'));\n }\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/logger/node/index.ts"],"sourcesContent":["import { writeSync } from 'node:fs';\nimport { stripVTControlCharacters } from 'node:util';\n\nimport { supportsLoggerColor } from '../_internals/formatter';\nimport { getLoggerStreamName } from '../_internals/levels';\nimport {\n createDiagnosticReporter,\n createLoggerCore,\n} from '../_internals/logger';\nimport type { LoggerDiagnostic } from '../types';\n\nimport { createFileWriter } from './_internals/file-writer';\nimport { formatNodeLoggerRecord } from './_internals/formatter';\nimport type {\n NodeLogger,\n NodeLoggerOptions,\n} from './types';\n\ninterface ConsoleStreamState {\n onClose: () => void;\n onError: (error: Error) => void;\n owners: Set<object>;\n pending: Set<(error?: Error | null) => void>;\n}\n\n// Constants/Variables\nconst consoleStreams = new WeakMap<NodeJS.WriteStream, ConsoleStreamState>();\n\n// Functions\n\n/**\n * Creates a Node.js/Bun logger with optional bounded, append-only file output and rotation.\n *\n * Log calls never throw or reject. Awaiting them waits for processing, not proof of successful\n * persistence: overload drops the newest file record and failures are reported safely. Factory\n * validation/open failures reject. Call close explicitly; no process signal handlers are installed.\n */\nexport async function createNodeLogger(options: NodeLoggerOptions = {}): Promise<Readonly<NodeLogger>> {\n const report = createDiagnosticReporter(options.onError, (message) => writeSync(2, message));\n let writer: Awaited<ReturnType<typeof createFileWriter>> | undefined;\n let closed = false;\n let closePromise: Promise<void> | undefined;\n const consoleOwner = {};\n const consoleTargets = new Set<NodeJS.WriteStream>();\n const consoleWrites = new Set<Promise<void>>();\n const logger = createLoggerCore(\n options,\n {\n report,\n async write(record) {\n if (closed) {\n report({\n message: 'Logger is closed.',\n type: 'closed',\n });\n\n return;\n }\n\n if (!record.console && !writer) return;\n const target = process[getLoggerStreamName(record.type)];\n\n const text = formatNodeLoggerRecord(record, record.console && supportsLoggerColor(target));\n if (record.console) {\n const line = `${text}\\n`;\n consoleTargets.add(target);\n const completion = writeConsole(target, line, report, consoleOwner);\n consoleWrites.add(completion);\n void completion.then(() => consoleWrites.delete(completion));\n const fileCompletion = writer?.write(`${stripVTControlCharacters(text)}\\n`);\n return fileCompletion\n ? await Promise.all([\n completion,\n fileCompletion,\n ]).then(() => {})\n : await completion;\n }\n\n return await writer?.write(`${stripVTControlCharacters(text)}\\n`);\n },\n },\n );\n\n if (options.file !== undefined && options.file !== false) writer = await createFileWriter(options.file, report);\n\n return Object.freeze({\n ...logger,\n async close() {\n if (closePromise) return await closePromise;\n closed = true;\n closePromise = (async () => {\n await Promise.all([\n writer?.close(),\n ...consoleWrites,\n ]);\n\n for (const target of consoleTargets) {\n const state = consoleStreams.get(target);\n if (!state) continue;\n state.owners.delete(consoleOwner);\n if (!state.owners.size) {\n target.off('error', state.onError);\n target.off('close', state.onClose);\n consoleStreams.delete(target);\n }\n }\n\n consoleTargets.clear();\n })();\n\n return await closePromise;\n },\n getStats: () => writer?.getStats() ?? Object.freeze({\n droppedCount: 0,\n pendingBytes: 0,\n writeErrorCount: 0,\n }),\n });\n}\n\n/**\n * One error listener per shared output stream; retained until its last owning logger calls close().\n */\nasync function writeConsole(\n target: NodeJS.WriteStream,\n text: string,\n report: (event: LoggerDiagnostic) => void,\n owner: object,\n) {\n let state = consoleStreams.get(target);\n if (!state) {\n const pending = new Set<(error?: Error | null) => void>();\n const onError = (error: Error) => {\n for (const settle of pending) settle(error);\n };\n\n const onClose = () => onError(new Error('Console stream closed.'));\n state = {\n onClose,\n onError,\n owners: new Set(),\n pending,\n };\n\n consoleStreams.set(target, state);\n target.on('error', onError);\n target.on('close', onClose);\n }\n\n state.owners.add(owner);\n const current = state;\n return await new Promise<void>((resolveWritten) => {\n let settled = false;\n const settle = (error?: Error | null) => {\n if (settled) return;\n settled = true;\n current.pending.delete(settle);\n if (error) {\n report({\n error,\n message: 'Console output failed.',\n type: 'console',\n });\n }\n\n resolveWritten();\n };\n\n current.pending.add(settle);\n try {\n target.write(text, settle);\n } catch (error) {\n settle(error instanceof Error ? error : new Error('Console output failed.'));\n }\n });\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kikiutils/shared",
3
3
  "type": "module",
4
- "version": "20.1.1",
4
+ "version": "20.1.2",
5
5
  "description": "A lightweight and modular utility library for modern JavaScript and TypeScript — includes secure hashing, flexible logging, datetime tools, Vue/web helpers, storage abstraction, and more.",
6
6
  "author": "kiki-kanri",
7
7
  "license": "MIT",
@@ -32,9 +32,6 @@
32
32
  "sideEffects": false,
33
33
  "exports": {
34
34
  "./buffer": "./dist/buffer.js",
35
- "./classes/enhanced-date": "./dist/classes/enhanced-date.js",
36
- "./classes/path": "./dist/classes/path.js",
37
- "./classes/precision-number": "./dist/classes/precision-number.js",
38
35
  "./clipboard": "./dist/clipboard.js",
39
36
  "./crypto-hash": "./dist/crypto-hash.js",
40
37
  "./datetime": "./dist/datetime.js",
@@ -47,24 +44,43 @@
47
44
  "./hash": "./dist/hash.js",
48
45
  "./logger": "./dist/logger/index.js",
49
46
  "./logger/node": "./dist/logger/node/index.js",
50
- "./logger/node/types": "./dist/logger/node/types.js",
51
- "./logger/types": "./dist/logger/types.js",
47
+ "./logger/node/types": {
48
+ "types": "./dist/logger/node/types.d.ts",
49
+ "default": null
50
+ },
51
+ "./logger/types": {
52
+ "types": "./dist/logger/types.d.ts",
53
+ "default": null
54
+ },
52
55
  "./math": "./dist/math.js",
53
56
  "./number": "./dist/number.js",
54
57
  "./random": "./dist/random.js",
55
- "./redis/script-runner": "./dist/redis/script-runner.js",
56
- "./storages/lru/keyed-store": "./dist/storages/lru/keyed-store.js",
57
- "./storages/redis/keyed-store": "./dist/storages/redis/keyed-store.js",
58
- "./storages/redis/msgpack": "./dist/storages/redis/msgpack.js",
59
- "./storages/redis/types": "./dist/storages/redis/types.js",
58
+ "./storages/redis/types": {
59
+ "types": "./dist/storages/redis/types.d.ts",
60
+ "default": null
61
+ },
60
62
  "./string": "./dist/string.js",
61
63
  "./time": "./dist/time.js",
62
- "./types": "./dist/types/index.js",
63
- "./types/filtered-key-path": "./dist/types/filtered-key-path.js",
64
- "./types/vue": "./dist/types/vue.js",
64
+ "./types": {
65
+ "types": "./dist/types/index.d.ts",
66
+ "default": null
67
+ },
65
68
  "./url": "./dist/url.js",
66
69
  "./vue": "./dist/vue.js",
67
70
  "./web": "./dist/web.js",
71
+ "./classes/*": "./dist/classes/*.js",
72
+ "./logger/*": "./dist/logger/*.js",
73
+ "./redis/*": "./dist/redis/*.js",
74
+ "./storages/*": "./dist/storages/*.js",
75
+ "./types/*": {
76
+ "types": "./dist/types/*.d.ts",
77
+ "default": null
78
+ },
79
+ "./logger/_internals/*": null,
80
+ "./logger/index": null,
81
+ "./logger/node/_internals/*": null,
82
+ "./logger/node/index": null,
83
+ "./types/index": null,
68
84
  "./package.json": "./package.json"
69
85
  },
70
86
  "files": [
@@ -150,7 +166,7 @@
150
166
  "devDependencies": {
151
167
  "@antfu/eslint-config": "^10.0.0",
152
168
  "@arethetypeswrong/core": "^0.18.5",
153
- "@kikiutils/eslint-config": "^11.0.0",
169
+ "@kikiutils/eslint-config": "^11.0.1",
154
170
  "@kikiutils/tsconfigs": "^7.0.0",
155
171
  "@noble/hashes": "^2.4.0",
156
172
  "@types/node": "^26.6.6",
@@ -165,17 +181,17 @@
165
181
  "eslint": "^10.14.0",
166
182
  "file-type": "^22.3.0",
167
183
  "jsdom": "^31.0.0",
168
- "knip": "^6.44.2",
184
+ "knip": "^6.45.1",
169
185
  "lru-cache": "^11.5.3",
170
186
  "millify": "^6.1.0",
171
187
  "msgpackr": "^2.1.4",
172
188
  "publint": "^0.3.25",
173
- "rotating-file-stream": "^3.2.10",
189
+ "rotating-file-stream": "^4.0.0",
174
190
  "tsdown": "^0.23.7",
175
191
  "typescript": "^6.0.3",
176
192
  "unplugin-unused": "^0.6.0",
177
193
  "vitest": "^5.1.0",
178
194
  "vue": "^3.5.45",
179
- "vue-router": "^5.4.0"
195
+ "vue-router": "^5.5.0"
180
196
  }
181
197
  }