@seedcord/utils 0.5.0 → 0.6.0-next.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +41 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +99 -25
- package/dist/index.mjs +40 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -112,13 +112,18 @@ function fyShuffle(items) {
|
|
|
112
112
|
return array;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/numbers/toEpochSeconds.ts
|
|
117
|
+
/** Converts absolute epoch milliseconds to epoch seconds, the unit Discord `<t:...>` timestamp markup reads. */
|
|
118
|
+
function toEpochSeconds(ms) {
|
|
119
|
+
return Math.round(ms / 1e3);
|
|
120
|
+
}
|
|
121
|
+
|
|
115
122
|
//#endregion
|
|
116
123
|
//#region src/numbers/currentTime.ts
|
|
117
|
-
/**
|
|
118
|
-
* Return current time in seconds
|
|
119
|
-
*/
|
|
124
|
+
/** Current time in epoch seconds. Shares the one ms→s rounding rule with {@link toEpochSeconds}. */
|
|
120
125
|
function currentTime() {
|
|
121
|
-
return
|
|
126
|
+
return toEpochSeconds(Date.now());
|
|
122
127
|
}
|
|
123
128
|
|
|
124
129
|
//#endregion
|
|
@@ -161,6 +166,34 @@ function ordinal(n) {
|
|
|
161
166
|
return `${n}${suffix}`;
|
|
162
167
|
}
|
|
163
168
|
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/numbers/parseDuration.ts
|
|
171
|
+
const UNIT_MS = {
|
|
172
|
+
ms: 1,
|
|
173
|
+
s: 1e3,
|
|
174
|
+
m: 6e4,
|
|
175
|
+
h: 36e5,
|
|
176
|
+
d: 864e5
|
|
177
|
+
};
|
|
178
|
+
const DURATION_PATTERN = new RegExp(`^(\\d+)(${Object.keys(UNIT_MS).join("|")})$`);
|
|
179
|
+
/**
|
|
180
|
+
* Parses a short duration string like `24h`, `30m`, `90s`, `7d`, or `500ms` into milliseconds.
|
|
181
|
+
*
|
|
182
|
+
* The grammar is one or more digits followed by one lowercase unit (`ms`, `s`, `m`, `h`, `d`).
|
|
183
|
+
* Anything else returns `null`, including a bare number, an unknown unit, an uppercase unit,
|
|
184
|
+
* surrounding whitespace, a fractional value, and a result of zero. The return is a positive
|
|
185
|
+
* number or `null`, never `0` or `NaN`, so a malformed input can never read as a valid duration.
|
|
186
|
+
*
|
|
187
|
+
* @param input - The duration string to parse.
|
|
188
|
+
* @returns The duration in milliseconds, or `null` if `input` is not a well-formed positive duration.
|
|
189
|
+
*/
|
|
190
|
+
function parseDuration(input) {
|
|
191
|
+
const match = DURATION_PATTERN.exec(input);
|
|
192
|
+
if (!match) return null;
|
|
193
|
+
const ms = Number(match[1]) * UNIT_MS[match[2]];
|
|
194
|
+
return ms > 0 ? ms : null;
|
|
195
|
+
}
|
|
196
|
+
|
|
164
197
|
//#endregion
|
|
165
198
|
//#region src/numbers/percentage.ts
|
|
166
199
|
/**
|
|
@@ -388,7 +421,7 @@ function hasKeys(obj, keys) {
|
|
|
388
421
|
* @typeParam TObject - the original object type you're pulling from
|
|
389
422
|
* @typeParam TKey - the keys to copy when defined
|
|
390
423
|
* @param source - the object to read values from
|
|
391
|
-
* @param keys - optional list of keys to include when present.
|
|
424
|
+
* @param keys - optional list of keys to include when present. {@default all keys}
|
|
392
425
|
*
|
|
393
426
|
* @example
|
|
394
427
|
* ```ts
|
|
@@ -536,7 +569,7 @@ function prettyDifference(numBefore, numAfter) {
|
|
|
536
569
|
//#endregion
|
|
537
570
|
//#region src/index.ts
|
|
538
571
|
/** Package version */
|
|
539
|
-
const version = "0.
|
|
572
|
+
const version = "0.6.0-next.0";
|
|
540
573
|
|
|
541
574
|
//#endregion
|
|
542
575
|
exports.assertNever = assertNever;
|
|
@@ -552,11 +585,13 @@ exports.isTsOrJsFile = isTsOrJsFile;
|
|
|
552
585
|
exports.keepDefined = keepDefined;
|
|
553
586
|
exports.longestStringLength = longestStringLength;
|
|
554
587
|
exports.ordinal = ordinal;
|
|
588
|
+
exports.parseDuration = parseDuration;
|
|
555
589
|
exports.percentage = percentage;
|
|
556
590
|
exports.prettify = prettify;
|
|
557
591
|
exports.prettyDifference = prettyDifference;
|
|
558
592
|
exports.round = round;
|
|
559
593
|
exports.roundToDenomination = roundToDenomination;
|
|
594
|
+
exports.toEpochSeconds = toEpochSeconds;
|
|
560
595
|
exports.traverseDirectory = traverseDirectory;
|
|
561
596
|
exports.version = version;
|
|
562
597
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path"],"sources":["../src/misc/assertNever.ts","../src/misc/directory.ts","../src/misc/fyShuffle.ts","../src/numbers/currentTime.ts","../src/numbers/generateCode.ts","../src/numbers/ordinal.ts","../src/numbers/percentage.ts","../src/numbers/round.ts","../src/numbers/roundToDenomination.ts","../src/objects/filterCirculars.ts","../src/objects/hasKeys.ts","../src/objects/keepDefined.ts","../src/strings/capitalize.ts","../src/strings/longestStringLength.ts","../src/strings/generateAsciiTable.ts","../src/strings/prettify.ts","../src/strings/prettyDifference.ts","../src/index.ts"],"sourcesContent":["/**\n * Exhaustiveness guard for discriminated unions. Place in the `default` branch of a `switch` over a\n * union's discriminant: if a new variant is added without a matching case, the call fails to compile.\n * Throws at runtime if reached with a value the types said was impossible.\n */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);\n}\n","import { readdir } from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport type { ILogger } from '@seedcord/types';\nimport type * as fs from 'node:fs';\n\n/**\n * Determines if a directory entry is a TypeScript or JavaScript file.\n *\n * @param entry - The directory entry to check.\n * @returns True if the entry is a file ending with .ts or .js.\n */\nexport function isTsOrJsFile(entry: fs.Dirent): boolean {\n return (\n entry.isFile() &&\n (entry.name.endsWith('.ts') || entry.name.endsWith('.js')) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.endsWith('.map')\n );\n}\n\n/**\n * Recursively traverses through a directory, importing all .ts and .js files and applying a callback to each import.\n *\n * @param dir - The directory path to traverse.\n * @param callback - A function that will be called for each imported module. It receives the full file path, the file's relative path, and the imported module as arguments.\n * @returns A Promise that resolves when the traversal is complete.\n */\nexport async function traverseDirectory(\n dir: string,\n callback: (fullPath: string, relativePath: string, imported: Record<string, unknown>) => Promise<void> | void,\n logger: ILogger\n): Promise<void> {\n let entries: fs.Dirent[];\n\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch (err) {\n logger.error(`Failed to read directory ${dir}`, err);\n entries = [];\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(process.cwd(), fullPath);\n\n if (entry.isDirectory()) {\n await traverseDirectory(fullPath, callback, logger);\n } else if (isTsOrJsFile(entry)) {\n const imported = (await import(fullPath)) as Record<string, unknown>;\n await callback(fullPath, relativePath, imported);\n }\n }\n}\n\n/**\n * Options for formatting file paths.\n */\nexport interface FormatFileOptions {\n /**\n * Whether to return only the directory part of the path. {@default false}\n */\n onlyDir?: boolean;\n /**\n * A prefix to prepend to the formatted path. {@default ./}\n */\n prefix?: string;\n}\n\n/**\n * Formats a file path relative to the current working directory.\n * @param filePath - The file path to format.\n * @param options - Formatting options.\n * @returns The formatted file path.\n */\nexport function formatFilePath(filePath: string, options: FormatFileOptions = {}): string {\n const { onlyDir = false, prefix = './' } = options;\n\n const resolved = onlyDir\n ? path.relative(process.cwd(), filePath.replace(/\\/[^/]*$/, ''))\n : path.relative(process.cwd(), filePath);\n return `${prefix}${resolved}`;\n}\n","/**\n * Shuffles an array using the Fisher-Yates algorithm.\n * This function creates a new array with the same elements in a random order,\n * without modifying the original array.\n *\n * @typeParam TArray - The type of elements in the array\n * @param items - The array to shuffle\n * @returns A new array with the same elements in a random order\n *\n * @example\n * ```typescript\n * const numbers = [1, 2, 3, 4, 5];\n * const shuffled = fyShuffle(numbers);\n * // shuffled might be [3, 1, 5, 2, 4]\n * // numbers is still [1, 2, 3, 4, 5]\n * ```\n */\nexport function fyShuffle<TArray>(items: TArray[]): TArray[] {\n const array = items.slice();\n for (let i = array.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n // @ts-expect-error - TypeScript doesn't recognize that TArray can be swapped\n [array[i], array[j]] = [array[j], array[i]];\n }\n return array;\n}\n","/**\n * Return current time in seconds\n */\nexport function currentTime(): number {\n return Math.floor(Date.now() / 1000);\n}\n","/**\n * Generates a random numeric code with the specified number of digits.\n *\n * @param digits - The number of digits for the generated code.\n * @returns A random numeric code with the specified number of digits.\n */\nexport function generateCode(digits: number): number {\n const min = Math.pow(10, digits - 1);\n const max = Math.pow(10, digits) - 1;\n return Math.floor(Math.random() * (max - min + 1) + min);\n}\n","/**\n * Returns the ordinal suffix for a given number.\n *\n * @param n - The number to get the ordinal for\n * @returns The number with its ordinal suffix\n *\n * @example\n * ordinal(1); // \"1st\"\n * ordinal(22); // \"22nd\"\n * ordinal(13); // \"13th\"\n */\nexport function ordinal(n: number): string {\n const s = ['th', 'st', 'nd', 'rd'];\n const v = n % 100;\n const index = (v - 20) % 10;\n const suffix = s[index] ?? s[v] ?? s[0];\n if (!suffix) return `${n}th`;\n\n return `${n}${suffix}`;\n}\n","/**\n * Takes two numbers and returns the percentage of the first number in the second number with two decimal places.\n *\n * @param num1 - The first number.\n * @param num2 - The second number.\n *\n * @returns The percentage of the first number in the second number with two decimal places.\n */\nexport function percentage(num1: number, num2: number): number {\n return Number(((num1 / num2) * 100).toFixed(2));\n}\n","/**\n * Rounds a number to a specified number of decimal places.\n *\n * @param num - The number to be rounded.\n * @param precision - The number of decimal places to round to.\n * @returns The rounded number.\n */\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round((num + Number.EPSILON) * factor) / factor;\n}\n","import type { TupleOf } from 'type-fest';\n\nexport interface RoundToDenomOptions {\n /**\n * Suffixes to use for each denomination level. Defaults to `['K', 'M', 'B', 'T', 'Q']`.\n */\n suffixes?: TupleOf<5, string>;\n /** Number of decimal places to include in the rounded result. Defaults to `1`. */\n precision?: number;\n}\n\n/**\n * Rounds a number to a string representation with a denomination suffix.\n * @param num - The number to round.\n * @example\n * ```ts\n * roundToDenomination(1234); // \"1.2K\"\n * roundToDenomination(10000, { suffixes: ['k', 'm', 'b', 't', 'q'] }); // \"10k\"\n * roundToDenomination(12345678); // \"12.3M\"\n * ```\n * @returns The rounded number as a string with a denomination suffix.\n */\nexport function roundToDenomination(num: number, opts?: RoundToDenomOptions): string {\n const { suffixes = ['K', 'M', 'B', 'T', 'Q'], precision = 1 } = opts ?? {};\n\n if (num < 10000) {\n return num.toString();\n }\n\n let index = -1;\n let temp = num;\n\n while (temp >= 1000 && index < suffixes.length - 1) {\n temp /= 1000;\n index++;\n }\n\n let result;\n\n if (temp % 1 === 0) {\n result = temp.toString();\n } else {\n const adjustedTemp = Math.round(temp * Math.pow(10, precision + 1)) / Math.pow(10, precision + 1);\n result = adjustedTemp.toFixed(precision);\n }\n\n if (result.endsWith('.9')) {\n result = Math.ceil(Number(result)).toString();\n }\n\n if (result.endsWith('.0')) {\n result = result.substring(0, result.length - 2);\n }\n\n if (result === '1000') {\n index += 1;\n result = '1';\n }\n\n return result + (index >= 0 ? suffixes[index] : '');\n}\n","import type { ILogger } from '@seedcord/types';\nimport type { JsonPrimitive } from 'type-fest';\n\n/**\n * JSONify an arbitrary type while allowing any object position to be replaced\n * by a circular marker. Optional keys stay optional.\n */\nexport type JsonifyWithCirculars<BaseType, Marker extends string = '[Circular]'> = BaseType extends JsonPrimitive\n ? BaseType\n : BaseType extends bigint\n ? string\n : BaseType extends Date\n ? string\n : BaseType extends { toJSON(): infer J }\n ? unknown extends J\n ? JsonifyObject<BaseType, Marker>\n : JsonifyWithCirculars<J, Marker>\n : BaseType extends readonly (infer U)[]\n ? (JsonifyWithCirculars<U, Marker> | Marker)[]\n : BaseType extends Map<infer K, infer V>\n ? [JsonifyWithCirculars<K, Marker> | Marker, JsonifyWithCirculars<V, Marker> | Marker][]\n : BaseType extends Set<infer U2>\n ? (JsonifyWithCirculars<U2, Marker> | Marker)[]\n : BaseType extends (...args: unknown[]) => unknown\n ? never\n : BaseType extends object\n ? JsonifyObject<BaseType, Marker>\n : never;\n\n/**\n * Helper to JSONify object types with circular markers.\n *\n * @internal\n */\nexport type JsonifyObject<BaseType, Marker extends string> = {\n [K in keyof BaseType as K extends symbol\n ? never\n : BaseType[K] extends (...args: unknown[]) => unknown\n ? never\n : K]:\n | JsonifyWithCirculars<Exclude<BaseType[K], undefined>, Marker>\n | Extract<BaseType[K], undefined>\n | Marker;\n};\n\n/**\n * Returned by {@link filterCirculars} when a value cannot be made JSON-safe. The original value is\n * never returned on failure, because it would re-throw in the caller's own `JSON.stringify`.\n */\nexport interface UnserializableValue {\n '[unserializable]': string;\n}\n\n/**\n * Configuration for {@link filterCirculars}.\n */\nexport interface FilterCircularsOptions<Marker extends string = '[Circular]'> {\n /** Optional {@link ILogger} used to log stringify or parse errors. */\n logger?: ILogger;\n /** Override the circular placeholder. Default is `[Circular]`. */\n marker?: Marker;\n /** Processing mode. `json` uses stringify and parse (might end up using a `toJSON()` if found). `decycle` builds a safe clone first. Default is `decycle`. */\n mode?: 'json' | 'decycle';\n}\n\n/**\n * Creates a clean, JSON safe copy of a value and replaces circular references with a marker.\n *\n * In `json` mode it behaves like stringify then parse with a replacer that handles cycles and BigInt.\n * In `decycle` mode it first clones without using toJSON, then you can stringify the result later.\n *\n * @typeParam ObjType - Type of the input value.\n * @typeParam Marker - Marker string used for circular references.\n *\n * @param value - The value to clone safely.\n * @param options - Optional configuration.\n *\n * @returns A JSON safe structure with circular references replaced by the marker.\n *\n * @example\n * ```ts\n * interface Test {\n * name: string;\n * self?: Test;\n * }\n *\n * const obj: Test = { name: 'seedcord' };\n * obj.self = obj;\n *\n * const clean = filterCirculars(obj);\n * // ^? { name: string; self?: \"[Circular]\" | { ... } }\n * console.log(clean.self); // \"[Circular]\"\n * ```\n */\nexport function filterCirculars<ObjType, Marker extends string = '[Circular]'>(\n value: ObjType,\n options?: FilterCircularsOptions<Marker>\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const logger = options?.logger;\n const marker = (options?.marker ?? '[Circular]') as Marker;\n const mode = options?.mode ?? 'decycle';\n\n if (mode === 'json') return json(value, marker, logger);\n\n try {\n return decycle(value, marker);\n } catch (error) {\n logger?.error('filterCirculars decycle error', error);\n return { '[unserializable]': 'decycle failed' };\n }\n}\n\n/**\n * Attempts to build a JSONified object using JSON.stringify and JSON.parse.\n *\n * @internal\n */\nfunction json<ObjType, Marker extends string>(\n value: ObjType,\n marker: Marker,\n logger?: ILogger\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const seen = new WeakSet<object>();\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value, (_k: string, v: unknown) => {\n if (typeof v === 'bigint') return v.toString();\n if (typeof v === 'object' && v !== null) {\n const obj = v;\n if (seen.has(obj)) return marker;\n seen.add(obj);\n }\n return v;\n });\n } catch (error) {\n logger?.error('filterCirculars stringify error', error);\n if (typeof value === 'object' && value !== null) {\n logger?.error('top level keys', Object.keys(value));\n }\n return { '[unserializable]': 'stringify failed' };\n }\n\n if (typeof json !== 'string') {\n return { '[unserializable]': 'stringify returned undefined' };\n }\n\n try {\n return JSON.parse(json) as JsonifyWithCirculars<ObjType, Marker>;\n } catch (error) {\n logger?.error('filterCirculars parse error', error);\n logger?.error('bad JSON', json);\n return { '[unserializable]': 'parse failed' };\n }\n}\n\n/**\n * Builds a JSON safe clone without calling toJSON on class instances.\n *\n * @internal\n */\nfunction decycle<ObjType, Marker extends string = '[Circular]'>(\n input: ObjType,\n marker: Marker,\n seen = new WeakSet<object>()\n): JsonifyWithCirculars<ObjType, Marker> {\n const recur = (val: unknown): unknown => {\n if (val === null) return null;\n const t = typeof val;\n\n if (t === 'bigint') return (val as bigint).toString();\n if (t !== 'object') return val;\n\n const obj = val as Record<string | number | symbol, unknown>;\n\n if (seen.has(obj)) return marker;\n seen.add(obj);\n\n if (obj instanceof Date) return obj.toISOString();\n if (obj instanceof RegExp) return obj.toString();\n\n if (Array.isArray(obj)) {\n return obj.map((item) => recur(item));\n }\n\n if (obj instanceof Map) {\n return Array.from(obj, ([k, v]) => [recur(k), recur(v)]);\n }\n\n if (obj instanceof Set) {\n return Array.from(obj, (v) => recur(v));\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (typeof v === 'function') continue;\n out[k] = recur(v);\n }\n\n return out;\n };\n\n return recur(input) as JsonifyWithCirculars<ObjType, Marker>;\n}\n","import type { UnionToIntersection } from 'type-fest';\n\n/**\n * Extracts the type of a nested property, distributing over unions.\n *\n * @internal\n */\nexport type DeepGet<Obj, Key extends string> = Obj extends unknown\n ? Key extends `${infer K}.${infer Rest}`\n ? K extends keyof Obj\n ? DeepGet<NonNullable<Obj[K]>, Rest>\n : never\n : Key extends keyof Obj\n ? Obj[Key]\n : never\n : never;\n\n/**\n * Converts a dot-notation path string into a nested object type with a specific leaf value.\n *\n * @internal\n */\nexport type PathToObj<Path extends string, Value> = Path extends `${infer Head}.${infer Tail}`\n ? { [K in Head]: PathToObj<Tail, Value> }\n : { [K in Path]: Value };\n\n/**\n * Checks for the presence of nested keys in an object that's possibly a distributed union, narrowing the object type accordingly.\n *\n * Checks if an object has the specified nested keys and that their values are not null or undefined. If they are not, the object type is narrowed to reflect the presence of these keys with their respective types from the original distributed union object.\n *\n * @param obj - The object to check.\n * @param keys - An array of dot-notation paths to check.\n * @returns True if all keys exist and are non-null/defined, narrowing the object type.\n *\n * @example\n * ```ts\n * interface Test {\n * a?: {\n * b?: {\n * c?: string;\n * };\n * } | null;\n * x?: number | null;\n * }\n *\n * const obj: Test = { a: { b: { c: 'hello' } }, x: 42 };\n *\n * if (hasKeys(obj, ['a.b.c', 'x'])) {\n * // Here, obj is narrowed to:\n * // {\n * // a: { b: { c: string } };\n * // x: number;\n * // }\n * console.log(obj.a.b.c.toUpperCase()); // Safe to access and use\n * console.log(obj.x.toFixed(2)); // Safe to access and use\n * }\n * ```\n */\nexport function hasKeys<Obj extends object, Keys extends string>(\n obj: Obj,\n keys: Keys[]\n): obj is Obj &\n UnionToIntersection<\n Keys extends unknown ? PathToObj<Keys, UnionToIntersection<NonNullable<DeepGet<Obj, Keys>>>> : never\n > {\n return keys.every((key) => {\n const parts = key.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (\n current === null ||\n current === undefined ||\n (typeof current !== 'object' && typeof current !== 'function')\n ) {\n return false;\n }\n if (!(part in current)) {\n return false;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current !== null && current !== undefined;\n });\n}\n","/**\n * Copies only the keys whose values are defined.\n *\n * @typeParam TObject - the original object type you're pulling from\n * @typeParam TKey - the keys to copy when defined\n * @param source - the object to read values from\n * @param keys - optional list of keys to include when present. If omitted, all keys are considered\n *\n * @example\n * ```ts\n * interface Config {\n * host?: string;\n * port?: number;\n * user?: string;\n * password?: string;\n * }\n *\n * const config: Config = {\n * host: 'localhost',\n * port: undefined,\n * user: 'admin',\n * password: undefined\n * };\n *\n * const definedConfig = keepDefined(config, 'host', 'port', 'user', 'password');\n * // Result: { host: 'localhost', user: 'admin' }\n * ```\n */\nexport function keepDefined<TObject extends object, TKey extends keyof TObject>(\n source: TObject,\n ...keys: readonly TKey[]\n): Partial<Pick<TObject, TKey extends never ? keyof TObject : TKey>> {\n const selectedKeys = keys.length > 0 ? keys : (Object.keys(source) as TKey[]);\n const result: Partial<TObject> = {};\n\n for (const key of selectedKeys) {\n const value = source[key];\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n}\n","/**\n * Returns the word with its first letter capitalized and the rest in lowercase.\n * @param word - The word to be formatted.\n * @returns The formatted word.\n */\nexport function capitalize(word: string): string {\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();\n}\n","/**\n * Function takes an array of strings or numbers and returns the number of characters in the longest string/number\n *\n * @param arr - The array of strings or numbers\n * @returns The length of the longest element when converted to string\n */\nexport function longestStringLength(arr: (string | number)[]): number {\n return Math.max(...arr.map((el) => el.toString().length));\n}\n","/**\n * Generates an ASCII table from the provided data.\n *\n * @param data - The data to be displayed in the table.\n * @returns The generated ASCII table as a string.\n */\nexport function generateAsciiTable(data: string[][]): string {\n if (data.length === 0) return '';\n\n const firstRow = data[0];\n if (!firstRow || firstRow.length === 0) return '';\n\n let table = '';\n const columnWidths: number[] = [];\n\n // Determine the maximum width for each column\n for (let i = 0; i < firstRow.length; i++) {\n let maxWidth = 0;\n for (const row of data) {\n const cell = row[i];\n if (cell !== undefined) maxWidth = Math.max(maxWidth, cell.length);\n }\n columnWidths.push(maxWidth);\n }\n\n // Function to create a horizontal line\n const createLine = (char: string, left: string, intersect: string, right: string): string => {\n let line = left;\n columnWidths.forEach((width, index) => {\n line += char.repeat(width + 2);\n if (index < columnWidths.length - 1) line += intersect;\n else line += right;\n });\n line += '\\n';\n return line;\n };\n\n // Top border\n table += createLine('═', '╔', '╦', '╗');\n\n data.forEach((row, rowIndex) => {\n // Row content\n table += '║';\n row.forEach((cell, columnIndex) => {\n const columnWidth = columnWidths[columnIndex];\n if (columnWidth !== undefined) table += ` ${cell.padEnd(columnWidth)} ║`;\n });\n table += '\\n';\n\n // Separator or bottom border\n if (rowIndex < data.length - 1) table += createLine('─', '╠', '╬', '╣');\n else table += createLine('═', '╚', '╩', '╝');\n });\n\n return table;\n}\n","import { capitalize } from './capitalize';\n\n/**\n * Options for the `prettify` function.\n */\nexport interface PrettifyOptions {\n capitalize?: boolean;\n}\n/**\n * Converts a string from any common naming convention to human-readable format.\n * Accepts camelCase, PascalCase, snake_case, and kebab-case input.\n *\n * @param key - The string to convert\n * @param opts - Optional configuration\n * @returns A space-separated, human-readable string\n *\n * @example\n * prettify(\"camelCaseString\") // \"camel Case String\"\n * prettify(\"PascalCaseString\") // \"Pascal Case String\"\n * prettify(\"snake_case_string\") // \"snake case string\"\n * prettify(\"kebab-case-string\") // \"kebab case string\"\n * prettify(\"mixedCase_string-name\") // \"mixed Case string name\"\n */\n\nexport function prettify(key: string, opts?: PrettifyOptions): string {\n const result = key\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase/PascalCase\n .replace(/[_-]/g, ' ') // snake_case and kebab-case\n .trim();\n\n if (opts?.capitalize) return capitalize(result);\n\n return result;\n}\n","/**\n * Calculates the difference between two numbers and formats it as a string with a '+' prefix for positive differences.\n *\n * @param numBefore - The initial number value\n * @param numAfter - The final number value\n * @returns A string representing the difference, with a '+' sign for positive differences\n *\n * @example\n * // Returns \"+5\"\n * prettyDifference(10, 15);\n *\n * @example\n * // Returns \"-3\"\n * prettyDifference(10, 7);\n */\nexport function prettyDifference(numBefore: number, numAfter: number): string {\n return (numAfter - numBefore > 0 ? `+${numAfter - numBefore}` : numAfter - numBefore).toString();\n}\n","export * from './misc';\nexport * from './numbers';\nexport * from './objects';\nexport * from './strings';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path"],"sources":["../src/misc/assertNever.ts","../src/misc/directory.ts","../src/misc/fyShuffle.ts","../src/numbers/toEpochSeconds.ts","../src/numbers/currentTime.ts","../src/numbers/generateCode.ts","../src/numbers/ordinal.ts","../src/numbers/parseDuration.ts","../src/numbers/percentage.ts","../src/numbers/round.ts","../src/numbers/roundToDenomination.ts","../src/objects/filterCirculars.ts","../src/objects/hasKeys.ts","../src/objects/keepDefined.ts","../src/strings/capitalize.ts","../src/strings/longestStringLength.ts","../src/strings/generateAsciiTable.ts","../src/strings/prettify.ts","../src/strings/prettyDifference.ts","../src/index.ts"],"sourcesContent":["/**\n * Exhaustiveness guard for discriminated unions. Place in the `default` branch of a `switch` over a\n * union's discriminant: if a new variant is added without a matching case, the call fails to compile.\n * Throws at runtime if reached with a value the types said was impossible.\n */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);\n}\n","import { readdir } from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport type { ILogger } from '@seedcord/types';\nimport type * as fs from 'node:fs';\n\n/**\n * Determines if a directory entry is a TypeScript or JavaScript file.\n *\n * @param entry - The directory entry to check.\n * @returns True if the entry is a file ending with .ts or .js.\n */\nexport function isTsOrJsFile(entry: fs.Dirent): boolean {\n return (\n entry.isFile() &&\n (entry.name.endsWith('.ts') || entry.name.endsWith('.js')) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.endsWith('.map')\n );\n}\n\n/**\n * Recursively traverses through a directory, importing all .ts and .js files and applying a callback to each import.\n *\n * @param dir - The directory path to traverse.\n * @param callback - A function that will be called for each imported module. It receives the full file path, the file's relative path, and the imported module as arguments.\n * @returns A Promise that resolves when the traversal is complete.\n */\nexport async function traverseDirectory(\n dir: string,\n callback: (fullPath: string, relativePath: string, imported: Record<string, unknown>) => Promise<void> | void,\n logger: ILogger\n): Promise<void> {\n let entries: fs.Dirent[];\n\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch (err) {\n logger.error(`Failed to read directory ${dir}`, err);\n entries = [];\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(process.cwd(), fullPath);\n\n if (entry.isDirectory()) {\n await traverseDirectory(fullPath, callback, logger);\n } else if (isTsOrJsFile(entry)) {\n const imported = (await import(fullPath)) as Record<string, unknown>;\n await callback(fullPath, relativePath, imported);\n }\n }\n}\n\n/**\n * Options for formatting file paths.\n */\nexport interface FormatFileOptions {\n /**\n * Whether to return only the directory part of the path.\n *\n * @defaultValue `false`\n */\n onlyDir?: boolean;\n /**\n * A prefix to prepend to the formatted path.\n *\n * @defaultValue `'./'`\n */\n prefix?: string;\n}\n\n/**\n * Formats a file path relative to the current working directory.\n * @param filePath - The file path to format.\n * @param options - Formatting options.\n * @returns The formatted file path.\n */\nexport function formatFilePath(filePath: string, options: FormatFileOptions = {}): string {\n const { onlyDir = false, prefix = './' } = options;\n\n const resolved = onlyDir\n ? path.relative(process.cwd(), filePath.replace(/\\/[^/]*$/, ''))\n : path.relative(process.cwd(), filePath);\n return `${prefix}${resolved}`;\n}\n","/**\n * Shuffles an array using the Fisher-Yates algorithm.\n * This function creates a new array with the same elements in a random order,\n * without modifying the original array.\n *\n * @typeParam TArray - The type of elements in the array\n * @param items - The array to shuffle\n * @returns A new array with the same elements in a random order\n *\n * @example\n * ```typescript\n * const numbers = [1, 2, 3, 4, 5];\n * const shuffled = fyShuffle(numbers);\n * // shuffled might be [3, 1, 5, 2, 4]\n * // numbers is still [1, 2, 3, 4, 5]\n * ```\n */\nexport function fyShuffle<TArray>(items: TArray[]): TArray[] {\n const array = items.slice();\n for (let i = array.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n // @ts-expect-error - TypeScript doesn't recognize that TArray can be swapped\n [array[i], array[j]] = [array[j], array[i]];\n }\n return array;\n}\n","import type { EpochMs, EpochSec } from '@seedcord/types';\n\n/** Converts absolute epoch milliseconds to epoch seconds, the unit Discord `<t:...>` timestamp markup reads. */\nexport function toEpochSeconds(ms: EpochMs): EpochSec {\n return Math.round(ms / 1000) as EpochSec;\n}\n","import { toEpochSeconds } from './toEpochSeconds';\n\nimport type { EpochMs, EpochSec } from '@seedcord/types';\n\n/** Current time in epoch seconds. Shares the one ms→s rounding rule with {@link toEpochSeconds}. */\nexport function currentTime(): EpochSec {\n return toEpochSeconds(Date.now() as EpochMs);\n}\n","/**\n * Generates a random numeric code with the specified number of digits.\n *\n * @param digits - The number of digits for the generated code.\n * @returns A random numeric code with the specified number of digits.\n */\nexport function generateCode(digits: number): number {\n const min = Math.pow(10, digits - 1);\n const max = Math.pow(10, digits) - 1;\n return Math.floor(Math.random() * (max - min + 1) + min);\n}\n","/**\n * Returns the ordinal suffix for a given number.\n *\n * @param n - The number to get the ordinal for\n * @returns The number with its ordinal suffix\n *\n * @example\n * ordinal(1); // \"1st\"\n * ordinal(22); // \"22nd\"\n * ordinal(13); // \"13th\"\n */\nexport function ordinal(n: number): string {\n const s = ['th', 'st', 'nd', 'rd'];\n const v = n % 100;\n const index = (v - 20) % 10;\n const suffix = s[index] ?? s[v] ?? s[0];\n if (!suffix) return `${n}th`;\n\n return `${n}${suffix}`;\n}\n","const UNIT_MS = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000\n} as const;\n\ntype DurationUnit = keyof typeof UNIT_MS;\n\n/** A duration literal like `30m`, `24h`, or `500ms`, a number followed by a unit (`ms`, `s`, `m`, `h`, `d`). */\nexport type ValidDuration = `${number}${DurationUnit}`;\n\nconst DURATION_PATTERN = new RegExp(`^(\\\\d+)(${Object.keys(UNIT_MS).join('|')})$`);\n\n/**\n * Parses a short duration string like `24h`, `30m`, `90s`, `7d`, or `500ms` into milliseconds.\n *\n * The grammar is one or more digits followed by one lowercase unit (`ms`, `s`, `m`, `h`, `d`).\n * Anything else returns `null`, including a bare number, an unknown unit, an uppercase unit,\n * surrounding whitespace, a fractional value, and a result of zero. The return is a positive\n * number or `null`, never `0` or `NaN`, so a malformed input can never read as a valid duration.\n *\n * @param input - The duration string to parse.\n * @returns The duration in milliseconds, or `null` if `input` is not a well-formed positive duration.\n */\nexport function parseDuration(input: string): number | null {\n const match = DURATION_PATTERN.exec(input);\n if (!match) return null;\n\n const ms = Number(match[1]) * UNIT_MS[match[2] as DurationUnit];\n return ms > 0 ? ms : null;\n}\n","/**\n * Takes two numbers and returns the percentage of the first number in the second number with two decimal places.\n *\n * @param num1 - The first number.\n * @param num2 - The second number.\n *\n * @returns The percentage of the first number in the second number with two decimal places.\n */\nexport function percentage(num1: number, num2: number): number {\n return Number(((num1 / num2) * 100).toFixed(2));\n}\n","/**\n * Rounds a number to a specified number of decimal places.\n *\n * @param num - The number to be rounded.\n * @param precision - The number of decimal places to round to.\n * @returns The rounded number.\n */\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round((num + Number.EPSILON) * factor) / factor;\n}\n","import type { TupleOf } from 'type-fest';\n\nexport interface RoundToDenomOptions {\n /**\n * Suffixes to use for each denomination level.\n *\n * @defaultValue `['K', 'M', 'B', 'T', 'Q']`\n */\n suffixes?: TupleOf<5, string>;\n /**\n * Number of decimal places to include in the rounded result.\n *\n * @defaultValue `1`\n */\n precision?: number;\n}\n\n/**\n * Rounds a number to a string representation with a denomination suffix.\n * @param num - The number to round.\n * @example\n * ```ts\n * roundToDenomination(1234); // \"1.2K\"\n * roundToDenomination(10000, { suffixes: ['k', 'm', 'b', 't', 'q'] }); // \"10k\"\n * roundToDenomination(12345678); // \"12.3M\"\n * ```\n * @returns The rounded number as a string with a denomination suffix.\n */\nexport function roundToDenomination(num: number, opts?: RoundToDenomOptions): string {\n const { suffixes = ['K', 'M', 'B', 'T', 'Q'], precision = 1 } = opts ?? {};\n\n if (num < 10000) {\n return num.toString();\n }\n\n let index = -1;\n let temp = num;\n\n while (temp >= 1000 && index < suffixes.length - 1) {\n temp /= 1000;\n index++;\n }\n\n let result;\n\n if (temp % 1 === 0) {\n result = temp.toString();\n } else {\n const adjustedTemp = Math.round(temp * Math.pow(10, precision + 1)) / Math.pow(10, precision + 1);\n result = adjustedTemp.toFixed(precision);\n }\n\n if (result.endsWith('.9')) {\n result = Math.ceil(Number(result)).toString();\n }\n\n if (result.endsWith('.0')) {\n result = result.substring(0, result.length - 2);\n }\n\n if (result === '1000') {\n index += 1;\n result = '1';\n }\n\n return result + (index >= 0 ? suffixes[index] : '');\n}\n","import type { ILogger } from '@seedcord/types';\nimport type { JsonPrimitive } from 'type-fest';\n\n/**\n * JSONify an arbitrary type while allowing any object position to be replaced\n * by a circular marker. Optional keys stay optional.\n */\nexport type JsonifyWithCirculars<BaseType, Marker extends string = '[Circular]'> = BaseType extends JsonPrimitive\n ? BaseType\n : BaseType extends bigint\n ? string\n : BaseType extends Date\n ? string\n : BaseType extends { toJSON(): infer J }\n ? unknown extends J\n ? JsonifyObject<BaseType, Marker>\n : JsonifyWithCirculars<J, Marker>\n : BaseType extends readonly (infer U)[]\n ? (JsonifyWithCirculars<U, Marker> | Marker)[]\n : BaseType extends Map<infer K, infer V>\n ? [JsonifyWithCirculars<K, Marker> | Marker, JsonifyWithCirculars<V, Marker> | Marker][]\n : BaseType extends Set<infer U2>\n ? (JsonifyWithCirculars<U2, Marker> | Marker)[]\n : BaseType extends (...args: unknown[]) => unknown\n ? never\n : BaseType extends object\n ? JsonifyObject<BaseType, Marker>\n : never;\n\n/**\n * Helper to JSONify object types with circular markers.\n *\n * @internal\n */\nexport type JsonifyObject<BaseType, Marker extends string> = {\n [K in keyof BaseType as K extends symbol\n ? never\n : BaseType[K] extends (...args: unknown[]) => unknown\n ? never\n : K]:\n | JsonifyWithCirculars<Exclude<BaseType[K], undefined>, Marker>\n | Extract<BaseType[K], undefined>\n | Marker;\n};\n\n/**\n * Returned by {@link filterCirculars} when a value cannot be made JSON-safe. The original value is\n * never returned on failure, because it would re-throw in the caller's own `JSON.stringify`.\n */\nexport interface UnserializableValue {\n '[unserializable]': string;\n}\n\n/**\n * Configuration for {@link filterCirculars}.\n */\nexport interface FilterCircularsOptions<Marker extends string = '[Circular]'> {\n /** Optional {@link ILogger} used to log stringify or parse errors. */\n logger?: ILogger;\n /**\n * Override the circular placeholder.\n *\n * @defaultValue `'[Circular]'`\n */\n marker?: Marker;\n /**\n * Processing mode. `json` uses stringify and parse (might end up using a `toJSON()` if found). `decycle` builds a safe clone first.\n *\n * @defaultValue `'decycle'`\n */\n mode?: 'json' | 'decycle';\n}\n\n/**\n * Creates a clean, JSON safe copy of a value and replaces circular references with a marker.\n *\n * In `json` mode it behaves like stringify then parse with a replacer that handles cycles and BigInt.\n * In `decycle` mode it first clones without using toJSON, then you can stringify the result later.\n *\n * @typeParam ObjType - Type of the input value.\n * @typeParam Marker - Marker string used for circular references.\n *\n * @param value - The value to clone safely.\n * @param options - Optional configuration.\n *\n * @returns A JSON safe structure with circular references replaced by the marker.\n *\n * @example\n * ```ts\n * interface Test {\n * name: string;\n * self?: Test;\n * }\n *\n * const obj: Test = { name: 'seedcord' };\n * obj.self = obj;\n *\n * const clean = filterCirculars(obj);\n * // ^? { name: string; self?: \"[Circular]\" | { ... } }\n * console.log(clean.self); // \"[Circular]\"\n * ```\n */\nexport function filterCirculars<ObjType, Marker extends string = '[Circular]'>(\n value: ObjType,\n options?: FilterCircularsOptions<Marker>\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const logger = options?.logger;\n const marker = (options?.marker ?? '[Circular]') as Marker;\n const mode = options?.mode ?? 'decycle';\n\n if (mode === 'json') return json(value, marker, logger);\n\n try {\n return decycle(value, marker);\n } catch (error) {\n logger?.error('filterCirculars decycle error', error);\n return { '[unserializable]': 'decycle failed' };\n }\n}\n\n/**\n * Attempts to build a JSONified object using JSON.stringify and JSON.parse.\n *\n * @internal\n */\nfunction json<ObjType, Marker extends string>(\n value: ObjType,\n marker: Marker,\n logger?: ILogger\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const seen = new WeakSet<object>();\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value, (_k: string, v: unknown) => {\n if (typeof v === 'bigint') return v.toString();\n if (typeof v === 'object' && v !== null) {\n const obj = v;\n if (seen.has(obj)) return marker;\n seen.add(obj);\n }\n return v;\n });\n } catch (error) {\n logger?.error('filterCirculars stringify error', error);\n if (typeof value === 'object' && value !== null) {\n logger?.error('top level keys', Object.keys(value));\n }\n return { '[unserializable]': 'stringify failed' };\n }\n\n if (typeof json !== 'string') {\n return { '[unserializable]': 'stringify returned undefined' };\n }\n\n try {\n return JSON.parse(json) as JsonifyWithCirculars<ObjType, Marker>;\n } catch (error) {\n logger?.error('filterCirculars parse error', error);\n logger?.error('bad JSON', json);\n return { '[unserializable]': 'parse failed' };\n }\n}\n\n/**\n * Builds a JSON safe clone without calling toJSON on class instances.\n *\n * @internal\n */\nfunction decycle<ObjType, Marker extends string = '[Circular]'>(\n input: ObjType,\n marker: Marker,\n seen = new WeakSet<object>()\n): JsonifyWithCirculars<ObjType, Marker> {\n const recur = (val: unknown): unknown => {\n if (val === null) return null;\n const t = typeof val;\n\n if (t === 'bigint') return (val as bigint).toString();\n if (t !== 'object') return val;\n\n const obj = val as Record<string | number | symbol, unknown>;\n\n if (seen.has(obj)) return marker;\n seen.add(obj);\n\n if (obj instanceof Date) return obj.toISOString();\n if (obj instanceof RegExp) return obj.toString();\n\n if (Array.isArray(obj)) {\n return obj.map((item) => recur(item));\n }\n\n if (obj instanceof Map) {\n return Array.from(obj, ([k, v]) => [recur(k), recur(v)]);\n }\n\n if (obj instanceof Set) {\n return Array.from(obj, (v) => recur(v));\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (typeof v === 'function') continue;\n out[k] = recur(v);\n }\n\n return out;\n };\n\n return recur(input) as JsonifyWithCirculars<ObjType, Marker>;\n}\n","import type { UnionToIntersection } from 'type-fest';\n\n/**\n * Extracts the type of a nested property, distributing over unions.\n *\n * @internal\n */\nexport type DeepGet<Obj, Key extends string> = Obj extends unknown\n ? Key extends `${infer K}.${infer Rest}`\n ? K extends keyof Obj\n ? DeepGet<NonNullable<Obj[K]>, Rest>\n : never\n : Key extends keyof Obj\n ? Obj[Key]\n : never\n : never;\n\n/**\n * Converts a dot-notation path string into a nested object type with a specific leaf value.\n *\n * @internal\n */\nexport type PathToObj<Path extends string, Value> = Path extends `${infer Head}.${infer Tail}`\n ? { [K in Head]: PathToObj<Tail, Value> }\n : { [K in Path]: Value };\n\n/**\n * Checks for the presence of nested keys in an object that's possibly a distributed union, narrowing the object type accordingly.\n *\n * Checks if an object has the specified nested keys and that their values are not null or undefined. If they are not, the object type is narrowed to reflect the presence of these keys with their respective types from the original distributed union object.\n *\n * @param obj - The object to check.\n * @param keys - An array of dot-notation paths to check.\n * @returns True if all keys exist and are non-null/defined, narrowing the object type.\n *\n * @example\n * ```ts\n * interface Test {\n * a?: {\n * b?: {\n * c?: string;\n * };\n * } | null;\n * x?: number | null;\n * }\n *\n * const obj: Test = { a: { b: { c: 'hello' } }, x: 42 };\n *\n * if (hasKeys(obj, ['a.b.c', 'x'])) {\n * // Here, obj is narrowed to:\n * // {\n * // a: { b: { c: string } };\n * // x: number;\n * // }\n * console.log(obj.a.b.c.toUpperCase()); // Safe to access and use\n * console.log(obj.x.toFixed(2)); // Safe to access and use\n * }\n * ```\n */\nexport function hasKeys<Obj extends object, Keys extends string>(\n obj: Obj,\n keys: Keys[]\n): obj is Obj &\n UnionToIntersection<\n Keys extends unknown ? PathToObj<Keys, UnionToIntersection<NonNullable<DeepGet<Obj, Keys>>>> : never\n > {\n return keys.every((key) => {\n const parts = key.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (\n current === null ||\n current === undefined ||\n (typeof current !== 'object' && typeof current !== 'function')\n ) {\n return false;\n }\n if (!(part in current)) {\n return false;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current !== null && current !== undefined;\n });\n}\n","/**\n * Copies only the keys whose values are defined.\n *\n * @typeParam TObject - the original object type you're pulling from\n * @typeParam TKey - the keys to copy when defined\n * @param source - the object to read values from\n * @param keys - optional list of keys to include when present. {@default all keys}\n *\n * @example\n * ```ts\n * interface Config {\n * host?: string;\n * port?: number;\n * user?: string;\n * password?: string;\n * }\n *\n * const config: Config = {\n * host: 'localhost',\n * port: undefined,\n * user: 'admin',\n * password: undefined\n * };\n *\n * const definedConfig = keepDefined(config, 'host', 'port', 'user', 'password');\n * // Result: { host: 'localhost', user: 'admin' }\n * ```\n */\nexport function keepDefined<TObject extends object, TKey extends keyof TObject>(\n source: TObject,\n ...keys: readonly TKey[]\n): Partial<Pick<TObject, TKey extends never ? keyof TObject : TKey>> {\n const selectedKeys = keys.length > 0 ? keys : (Object.keys(source) as TKey[]);\n const result: Partial<TObject> = {};\n\n for (const key of selectedKeys) {\n const value = source[key];\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n}\n","/**\n * Returns the word with its first letter capitalized and the rest in lowercase.\n * @param word - The word to be formatted.\n * @returns The formatted word.\n */\nexport function capitalize(word: string): string {\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();\n}\n","/**\n * Function takes an array of strings or numbers and returns the number of characters in the longest string/number\n *\n * @param arr - The array of strings or numbers\n * @returns The length of the longest element when converted to string\n */\nexport function longestStringLength(arr: (string | number)[]): number {\n return Math.max(...arr.map((el) => el.toString().length));\n}\n","/**\n * Generates an ASCII table from the provided data.\n *\n * @param data - The data to be displayed in the table.\n * @returns The generated ASCII table as a string.\n */\nexport function generateAsciiTable(data: string[][]): string {\n if (data.length === 0) return '';\n\n const firstRow = data[0];\n if (!firstRow || firstRow.length === 0) return '';\n\n let table = '';\n const columnWidths: number[] = [];\n\n // Determine the maximum width for each column\n for (let i = 0; i < firstRow.length; i++) {\n let maxWidth = 0;\n for (const row of data) {\n const cell = row[i];\n if (cell !== undefined) maxWidth = Math.max(maxWidth, cell.length);\n }\n columnWidths.push(maxWidth);\n }\n\n // Function to create a horizontal line\n const createLine = (char: string, left: string, intersect: string, right: string): string => {\n let line = left;\n columnWidths.forEach((width, index) => {\n line += char.repeat(width + 2);\n if (index < columnWidths.length - 1) line += intersect;\n else line += right;\n });\n line += '\\n';\n return line;\n };\n\n // Top border\n table += createLine('═', '╔', '╦', '╗');\n\n data.forEach((row, rowIndex) => {\n // Row content\n table += '║';\n row.forEach((cell, columnIndex) => {\n const columnWidth = columnWidths[columnIndex];\n if (columnWidth !== undefined) table += ` ${cell.padEnd(columnWidth)} ║`;\n });\n table += '\\n';\n\n // Separator or bottom border\n if (rowIndex < data.length - 1) table += createLine('─', '╠', '╬', '╣');\n else table += createLine('═', '╚', '╩', '╝');\n });\n\n return table;\n}\n","import { capitalize } from './capitalize';\n\n/**\n * Options for the `prettify` function.\n */\nexport interface PrettifyOptions {\n capitalize?: boolean;\n}\n/**\n * Converts a string from any common naming convention to human-readable format.\n * Accepts camelCase, PascalCase, snake_case, and kebab-case input.\n *\n * @param key - The string to convert\n * @param opts - Optional configuration\n * @returns A space-separated, human-readable string\n *\n * @example\n * prettify(\"camelCaseString\") // \"camel Case String\"\n * prettify(\"PascalCaseString\") // \"Pascal Case String\"\n * prettify(\"snake_case_string\") // \"snake case string\"\n * prettify(\"kebab-case-string\") // \"kebab case string\"\n * prettify(\"mixedCase_string-name\") // \"mixed Case string name\"\n */\n\nexport function prettify(key: string, opts?: PrettifyOptions): string {\n const result = key\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase/PascalCase\n .replace(/[_-]/g, ' ') // snake_case and kebab-case\n .trim();\n\n if (opts?.capitalize) return capitalize(result);\n\n return result;\n}\n","/**\n * Calculates the difference between two numbers and formats it as a string with a '+' prefix for positive differences.\n *\n * @param numBefore - The initial number value\n * @param numAfter - The final number value\n * @returns A string representing the difference, with a '+' sign for positive differences\n *\n * @example\n * // Returns \"+5\"\n * prettyDifference(10, 15);\n *\n * @example\n * // Returns \"-3\"\n * prettyDifference(10, 7);\n */\nexport function prettyDifference(numBefore: number, numAfter: number): string {\n return (numAfter - numBefore > 0 ? `+${numAfter - numBefore}` : numAfter - numBefore).toString();\n}\n","export * from './misc';\nexport * from './numbers';\nexport * from './objects';\nexport * from './strings';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ILogger } from "@seedcord/types";
|
|
1
|
+
import { EpochMs, EpochSec, ILogger } from "@seedcord/types";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
|
|
4
4
|
//#region src/misc/assertNever.d.ts
|
|
@@ -30,11 +30,15 @@ declare function traverseDirectory(dir: string, callback: (fullPath: string, rel
|
|
|
30
30
|
*/
|
|
31
31
|
interface FormatFileOptions {
|
|
32
32
|
/**
|
|
33
|
-
* Whether to return only the directory part of the path.
|
|
33
|
+
* Whether to return only the directory part of the path.
|
|
34
|
+
*
|
|
35
|
+
* @defaultValue `false`
|
|
34
36
|
*/
|
|
35
37
|
onlyDir?: boolean;
|
|
36
38
|
/**
|
|
37
|
-
* A prefix to prepend to the formatted path.
|
|
39
|
+
* A prefix to prepend to the formatted path.
|
|
40
|
+
*
|
|
41
|
+
* @defaultValue `'./'`
|
|
38
42
|
*/
|
|
39
43
|
prefix?: string;
|
|
40
44
|
}
|
|
@@ -67,10 +71,8 @@ declare function formatFilePath(filePath: string, options?: FormatFileOptions):
|
|
|
67
71
|
declare function fyShuffle<TArray>(items: TArray[]): TArray[];
|
|
68
72
|
//#endregion
|
|
69
73
|
//#region src/numbers/currentTime.d.ts
|
|
70
|
-
/**
|
|
71
|
-
|
|
72
|
-
*/
|
|
73
|
-
declare function currentTime(): number;
|
|
74
|
+
/** Current time in epoch seconds. Shares the one ms→s rounding rule with {@link toEpochSeconds}. */
|
|
75
|
+
declare function currentTime(): EpochSec;
|
|
74
76
|
//#endregion
|
|
75
77
|
//#region src/numbers/generateCode.d.ts
|
|
76
78
|
/**
|
|
@@ -95,6 +97,30 @@ declare function generateCode(digits: number): number;
|
|
|
95
97
|
*/
|
|
96
98
|
declare function ordinal(n: number): string;
|
|
97
99
|
//#endregion
|
|
100
|
+
//#region src/numbers/parseDuration.d.ts
|
|
101
|
+
declare const UNIT_MS: {
|
|
102
|
+
readonly ms: 1;
|
|
103
|
+
readonly s: 1000;
|
|
104
|
+
readonly m: 60000;
|
|
105
|
+
readonly h: 3600000;
|
|
106
|
+
readonly d: 86400000;
|
|
107
|
+
};
|
|
108
|
+
type DurationUnit = keyof typeof UNIT_MS;
|
|
109
|
+
/** A duration literal like `30m`, `24h`, or `500ms`, a number followed by a unit (`ms`, `s`, `m`, `h`, `d`). */
|
|
110
|
+
type ValidDuration = `${number}${DurationUnit}`;
|
|
111
|
+
/**
|
|
112
|
+
* Parses a short duration string like `24h`, `30m`, `90s`, `7d`, or `500ms` into milliseconds.
|
|
113
|
+
*
|
|
114
|
+
* The grammar is one or more digits followed by one lowercase unit (`ms`, `s`, `m`, `h`, `d`).
|
|
115
|
+
* Anything else returns `null`, including a bare number, an unknown unit, an uppercase unit,
|
|
116
|
+
* surrounding whitespace, a fractional value, and a result of zero. The return is a positive
|
|
117
|
+
* number or `null`, never `0` or `NaN`, so a malformed input can never read as a valid duration.
|
|
118
|
+
*
|
|
119
|
+
* @param input - The duration string to parse.
|
|
120
|
+
* @returns The duration in milliseconds, or `null` if `input` is not a well-formed positive duration.
|
|
121
|
+
*/
|
|
122
|
+
declare function parseDuration(input: string): number | null;
|
|
123
|
+
//#endregion
|
|
98
124
|
//#region src/numbers/percentage.d.ts
|
|
99
125
|
/**
|
|
100
126
|
* Takes two numbers and returns the percentage of the first number in the second number with two decimal places.
|
|
@@ -116,7 +142,7 @@ declare function percentage(num1: number, num2: number): number;
|
|
|
116
142
|
*/
|
|
117
143
|
declare function round(num: number, precision: number): number;
|
|
118
144
|
//#endregion
|
|
119
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
145
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/json-value.d.ts
|
|
120
146
|
/**
|
|
121
147
|
Matches any valid JSON primitive value.
|
|
122
148
|
|
|
@@ -124,7 +150,24 @@ Matches any valid JSON primitive value.
|
|
|
124
150
|
*/
|
|
125
151
|
type JsonPrimitive = string | number | boolean | null;
|
|
126
152
|
//#endregion
|
|
127
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
153
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/characters.d.ts
|
|
154
|
+
/**
|
|
155
|
+
Matches any digit as a string ('0'-'9').
|
|
156
|
+
|
|
157
|
+
@example
|
|
158
|
+
```
|
|
159
|
+
import type {DigitCharacter} from 'type-fest';
|
|
160
|
+
|
|
161
|
+
const a: DigitCharacter = '0'; // Valid
|
|
162
|
+
// @ts-expect-error
|
|
163
|
+
const b: DigitCharacter = 0; // Invalid
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
@category Type
|
|
167
|
+
*/
|
|
168
|
+
type DigitCharacter = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9';
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/union-to-intersection.d.ts
|
|
128
171
|
/**
|
|
129
172
|
Convert a union type to an intersection type.
|
|
130
173
|
|
|
@@ -153,7 +196,7 @@ Union extends unknown // The union type is used as the only argument to a functi
|
|
|
153
196
|
) extends ((mergedIntersection: infer Intersection) => void) // The `& Union` is to ensure result of `UnionToIntersection<A | B>` is always assignable to `A | B`
|
|
154
197
|
? Intersection & Union : never;
|
|
155
198
|
//#endregion
|
|
156
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
199
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/is-any.d.ts
|
|
157
200
|
/**
|
|
158
201
|
Returns a boolean for whether the given type is `any`.
|
|
159
202
|
|
|
@@ -184,7 +227,7 @@ const anyA = get(anyObject, 'a');
|
|
|
184
227
|
*/
|
|
185
228
|
type IsAny<T> = 0 extends 1 & NoInfer<T> ? true : false;
|
|
186
229
|
//#endregion
|
|
187
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
230
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/is-never.d.ts
|
|
188
231
|
/**
|
|
189
232
|
Returns a boolean for whether the given type is `never`.
|
|
190
233
|
|
|
@@ -240,7 +283,7 @@ type B = IsTrueFixed<never>;
|
|
|
240
283
|
*/
|
|
241
284
|
type IsNever<T> = [T] extends [never] ? true : false;
|
|
242
285
|
//#endregion
|
|
243
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
286
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/if.d.ts
|
|
244
287
|
/**
|
|
245
288
|
An if-else-like type that resolves depending on whether the given `boolean` type is `true` or `false`.
|
|
246
289
|
|
|
@@ -335,7 +378,7 @@ type Works = IncludesWithoutIf<HundredZeroes, '1'>;
|
|
|
335
378
|
*/
|
|
336
379
|
type If<Type extends boolean, IfBranch, ElseBranch> = IsNever<Type> extends true ? ElseBranch : Type extends true ? IfBranch : ElseBranch;
|
|
337
380
|
//#endregion
|
|
338
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
381
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/unknown-array.d.ts
|
|
339
382
|
/**
|
|
340
383
|
Represents an array with `unknown` value.
|
|
341
384
|
|
|
@@ -362,7 +405,7 @@ type C = IsArray<string>;
|
|
|
362
405
|
*/
|
|
363
406
|
type UnknownArray = readonly unknown[];
|
|
364
407
|
//#endregion
|
|
365
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
408
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/internal/type.d.ts
|
|
366
409
|
/**
|
|
367
410
|
An if-else-like type that resolves depending on whether the given type is `any` or `never`.
|
|
368
411
|
|
|
@@ -409,7 +452,7 @@ type T2 = TrimLeftOptimised<NineHundredNinetyNineSpaces>;
|
|
|
409
452
|
*/
|
|
410
453
|
type IfNotAnyOrNever<T, IfNotAnyOrNever, IfAny = any, IfNever = never> = If<IsAny<T>, IfAny, If<IsNever<T>, IfNever, IfNotAnyOrNever>>;
|
|
411
454
|
//#endregion
|
|
412
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
455
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/numeric.d.ts
|
|
413
456
|
type _Numeric = number | bigint;
|
|
414
457
|
type Zero = 0 | 0n;
|
|
415
458
|
/**
|
|
@@ -451,7 +494,7 @@ type ShouldBeTrue = IsNegative<-1>;
|
|
|
451
494
|
*/
|
|
452
495
|
type IsNegative<T extends _Numeric> = T extends Negative<T> ? true : false;
|
|
453
496
|
//#endregion
|
|
454
|
-
//#region ../../node_modules/.pnpm/type-fest@5.
|
|
497
|
+
//#region ../../node_modules/.pnpm/type-fest@5.7.0/node_modules/type-fest/source/tuple-of.d.ts
|
|
455
498
|
/**
|
|
456
499
|
Create a tuple type of the specified length with elements of the specified type.
|
|
457
500
|
|
|
@@ -479,7 +522,7 @@ type ZeroToFour = Range<0, 5>;
|
|
|
479
522
|
//=> '0' | '1' | '2' | '3' | '4'
|
|
480
523
|
|
|
481
524
|
type ThreeToEight = Range<3, 9>;
|
|
482
|
-
//=> '
|
|
525
|
+
//=> '3' | '4' | '5' | '6' | '7' | '8'
|
|
483
526
|
```
|
|
484
527
|
|
|
485
528
|
Note: If the specified length is the non-literal `number` type, the result will not be a tuple but a regular array.
|
|
@@ -512,20 +555,39 @@ type EmptyTuple = TupleOf<-3, string>;
|
|
|
512
555
|
//=> []
|
|
513
556
|
```
|
|
514
557
|
|
|
558
|
+
Note: If the specified length has a decimal part, the decimal part will be ignored.
|
|
559
|
+
|
|
560
|
+
@example
|
|
561
|
+
```
|
|
562
|
+
import type {TupleOf} from 'type-fest';
|
|
563
|
+
|
|
564
|
+
type DecimalLength = TupleOf<3.5, string>;
|
|
565
|
+
//=> [string, string, string]
|
|
566
|
+
```
|
|
567
|
+
|
|
515
568
|
Note: If you need a readonly tuple, simply wrap this type with `Readonly`, for example, to create `readonly [number, number, number]` use `Readonly<TupleOf<3, number>>`.
|
|
516
569
|
|
|
517
570
|
@category Array
|
|
518
571
|
*/
|
|
519
|
-
type TupleOf<Length extends number, Fill = unknown> = IfNotAnyOrNever<Length, _TupleOf<If<IsNegative<Length>, 0, Length>, Fill
|
|
520
|
-
type _TupleOf<
|
|
572
|
+
type TupleOf<Length extends number, Fill = unknown> = IfNotAnyOrNever<Length, _TupleOf<If<IsNegative<Length>, 0, Length>, Fill>, Fill[], []>;
|
|
573
|
+
type _TupleOf<Length extends number, Fill> = number extends Length ? Fill[] : BuildTupleDigitByDigit<`${Length}`, Fill>;
|
|
574
|
+
type BuildTupleDigitByDigit<Length extends string, Fill, Accumulator extends UnknownArray = []> = Length extends `${infer First extends DigitCharacter}${infer Rest}` ? BuildTupleDigitByDigit<Rest, Fill, [...RepeatTupleTenTimes<Accumulator>, ...DigitTupleOf<First, Fill>]> : Accumulator;
|
|
575
|
+
type RepeatTupleTenTimes<Tuple extends UnknownArray> = [...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple, ...Tuple];
|
|
576
|
+
type DigitTupleOf<Digit extends DigitCharacter, Fill> = [[], [Fill], [Fill, Fill], [Fill, Fill, Fill], [Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill], [Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill, Fill]][Digit];
|
|
521
577
|
//#endregion
|
|
522
578
|
//#region src/numbers/roundToDenomination.d.ts
|
|
523
579
|
interface RoundToDenomOptions {
|
|
524
580
|
/**
|
|
525
|
-
* Suffixes to use for each denomination level.
|
|
581
|
+
* Suffixes to use for each denomination level.
|
|
582
|
+
*
|
|
583
|
+
* @defaultValue `['K', 'M', 'B', 'T', 'Q']`
|
|
526
584
|
*/
|
|
527
585
|
suffixes?: TupleOf<5, string>;
|
|
528
|
-
/**
|
|
586
|
+
/**
|
|
587
|
+
* Number of decimal places to include in the rounded result.
|
|
588
|
+
*
|
|
589
|
+
* @defaultValue `1`
|
|
590
|
+
*/
|
|
529
591
|
precision?: number;
|
|
530
592
|
}
|
|
531
593
|
/**
|
|
@@ -541,6 +603,10 @@ interface RoundToDenomOptions {
|
|
|
541
603
|
*/
|
|
542
604
|
declare function roundToDenomination(num: number, opts?: RoundToDenomOptions): string;
|
|
543
605
|
//#endregion
|
|
606
|
+
//#region src/numbers/toEpochSeconds.d.ts
|
|
607
|
+
/** Converts absolute epoch milliseconds to epoch seconds, the unit Discord `<t:...>` timestamp markup reads. */
|
|
608
|
+
declare function toEpochSeconds(ms: EpochMs): EpochSec;
|
|
609
|
+
//#endregion
|
|
544
610
|
//#region src/objects/filterCirculars.d.ts
|
|
545
611
|
/**
|
|
546
612
|
* JSONify an arbitrary type while allowing any object position to be replaced
|
|
@@ -568,9 +634,17 @@ interface UnserializableValue {
|
|
|
568
634
|
interface FilterCircularsOptions<Marker extends string = '[Circular]'> {
|
|
569
635
|
/** Optional {@link ILogger} used to log stringify or parse errors. */
|
|
570
636
|
logger?: ILogger;
|
|
571
|
-
/**
|
|
637
|
+
/**
|
|
638
|
+
* Override the circular placeholder.
|
|
639
|
+
*
|
|
640
|
+
* @defaultValue `'[Circular]'`
|
|
641
|
+
*/
|
|
572
642
|
marker?: Marker;
|
|
573
|
-
/**
|
|
643
|
+
/**
|
|
644
|
+
* Processing mode. `json` uses stringify and parse (might end up using a `toJSON()` if found). `decycle` builds a safe clone first.
|
|
645
|
+
*
|
|
646
|
+
* @defaultValue `'decycle'`
|
|
647
|
+
*/
|
|
574
648
|
mode?: 'json' | 'decycle';
|
|
575
649
|
}
|
|
576
650
|
/**
|
|
@@ -659,7 +733,7 @@ declare function hasKeys<Obj extends object, Keys extends string>(obj: Obj, keys
|
|
|
659
733
|
* @typeParam TObject - the original object type you're pulling from
|
|
660
734
|
* @typeParam TKey - the keys to copy when defined
|
|
661
735
|
* @param source - the object to read values from
|
|
662
|
-
* @param keys - optional list of keys to include when present.
|
|
736
|
+
* @param keys - optional list of keys to include when present. {@default all keys}
|
|
663
737
|
*
|
|
664
738
|
* @example
|
|
665
739
|
* ```ts
|
|
@@ -755,5 +829,5 @@ declare function prettyDifference(numBefore: number, numAfter: number): string;
|
|
|
755
829
|
/** Package version */
|
|
756
830
|
declare const version: string;
|
|
757
831
|
//#endregion
|
|
758
|
-
export { DeepGet, FilterCircularsOptions, FormatFileOptions, JsonifyObject, JsonifyWithCirculars, PathToObj, PrettifyOptions, RoundToDenomOptions, UnserializableValue, assertNever, capitalize, currentTime, filterCirculars, formatFilePath, fyShuffle, generateAsciiTable, generateCode, hasKeys, isTsOrJsFile, keepDefined, longestStringLength, ordinal, percentage, prettify, prettyDifference, round, roundToDenomination, traverseDirectory, version };
|
|
832
|
+
export { DeepGet, FilterCircularsOptions, FormatFileOptions, JsonifyObject, JsonifyWithCirculars, PathToObj, PrettifyOptions, RoundToDenomOptions, UnserializableValue, ValidDuration, assertNever, capitalize, currentTime, filterCirculars, formatFilePath, fyShuffle, generateAsciiTable, generateCode, hasKeys, isTsOrJsFile, keepDefined, longestStringLength, ordinal, parseDuration, percentage, prettify, prettyDifference, round, roundToDenomination, toEpochSeconds, traverseDirectory, version };
|
|
759
833
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -83,13 +83,18 @@ function fyShuffle(items) {
|
|
|
83
83
|
return array;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/numbers/toEpochSeconds.ts
|
|
88
|
+
/** Converts absolute epoch milliseconds to epoch seconds, the unit Discord `<t:...>` timestamp markup reads. */
|
|
89
|
+
function toEpochSeconds(ms) {
|
|
90
|
+
return Math.round(ms / 1e3);
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
//#endregion
|
|
87
94
|
//#region src/numbers/currentTime.ts
|
|
88
|
-
/**
|
|
89
|
-
* Return current time in seconds
|
|
90
|
-
*/
|
|
95
|
+
/** Current time in epoch seconds. Shares the one ms→s rounding rule with {@link toEpochSeconds}. */
|
|
91
96
|
function currentTime() {
|
|
92
|
-
return
|
|
97
|
+
return toEpochSeconds(Date.now());
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
//#endregion
|
|
@@ -132,6 +137,34 @@ function ordinal(n) {
|
|
|
132
137
|
return `${n}${suffix}`;
|
|
133
138
|
}
|
|
134
139
|
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/numbers/parseDuration.ts
|
|
142
|
+
const UNIT_MS = {
|
|
143
|
+
ms: 1,
|
|
144
|
+
s: 1e3,
|
|
145
|
+
m: 6e4,
|
|
146
|
+
h: 36e5,
|
|
147
|
+
d: 864e5
|
|
148
|
+
};
|
|
149
|
+
const DURATION_PATTERN = new RegExp(`^(\\d+)(${Object.keys(UNIT_MS).join("|")})$`);
|
|
150
|
+
/**
|
|
151
|
+
* Parses a short duration string like `24h`, `30m`, `90s`, `7d`, or `500ms` into milliseconds.
|
|
152
|
+
*
|
|
153
|
+
* The grammar is one or more digits followed by one lowercase unit (`ms`, `s`, `m`, `h`, `d`).
|
|
154
|
+
* Anything else returns `null`, including a bare number, an unknown unit, an uppercase unit,
|
|
155
|
+
* surrounding whitespace, a fractional value, and a result of zero. The return is a positive
|
|
156
|
+
* number or `null`, never `0` or `NaN`, so a malformed input can never read as a valid duration.
|
|
157
|
+
*
|
|
158
|
+
* @param input - The duration string to parse.
|
|
159
|
+
* @returns The duration in milliseconds, or `null` if `input` is not a well-formed positive duration.
|
|
160
|
+
*/
|
|
161
|
+
function parseDuration(input) {
|
|
162
|
+
const match = DURATION_PATTERN.exec(input);
|
|
163
|
+
if (!match) return null;
|
|
164
|
+
const ms = Number(match[1]) * UNIT_MS[match[2]];
|
|
165
|
+
return ms > 0 ? ms : null;
|
|
166
|
+
}
|
|
167
|
+
|
|
135
168
|
//#endregion
|
|
136
169
|
//#region src/numbers/percentage.ts
|
|
137
170
|
/**
|
|
@@ -359,7 +392,7 @@ function hasKeys(obj, keys) {
|
|
|
359
392
|
* @typeParam TObject - the original object type you're pulling from
|
|
360
393
|
* @typeParam TKey - the keys to copy when defined
|
|
361
394
|
* @param source - the object to read values from
|
|
362
|
-
* @param keys - optional list of keys to include when present.
|
|
395
|
+
* @param keys - optional list of keys to include when present. {@default all keys}
|
|
363
396
|
*
|
|
364
397
|
* @example
|
|
365
398
|
* ```ts
|
|
@@ -507,8 +540,8 @@ function prettyDifference(numBefore, numAfter) {
|
|
|
507
540
|
//#endregion
|
|
508
541
|
//#region src/index.ts
|
|
509
542
|
/** Package version */
|
|
510
|
-
const version = "0.
|
|
543
|
+
const version = "0.6.0-next.0";
|
|
511
544
|
|
|
512
545
|
//#endregion
|
|
513
|
-
export { assertNever, capitalize, currentTime, filterCirculars, formatFilePath, fyShuffle, generateAsciiTable, generateCode, hasKeys, isTsOrJsFile, keepDefined, longestStringLength, ordinal, percentage, prettify, prettyDifference, round, roundToDenomination, traverseDirectory, version };
|
|
546
|
+
export { assertNever, capitalize, currentTime, filterCirculars, formatFilePath, fyShuffle, generateAsciiTable, generateCode, hasKeys, isTsOrJsFile, keepDefined, longestStringLength, ordinal, parseDuration, percentage, prettify, prettyDifference, round, roundToDenomination, toEpochSeconds, traverseDirectory, version };
|
|
514
547
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/misc/assertNever.ts","../src/misc/directory.ts","../src/misc/fyShuffle.ts","../src/numbers/currentTime.ts","../src/numbers/generateCode.ts","../src/numbers/ordinal.ts","../src/numbers/percentage.ts","../src/numbers/round.ts","../src/numbers/roundToDenomination.ts","../src/objects/filterCirculars.ts","../src/objects/hasKeys.ts","../src/objects/keepDefined.ts","../src/strings/capitalize.ts","../src/strings/longestStringLength.ts","../src/strings/generateAsciiTable.ts","../src/strings/prettify.ts","../src/strings/prettyDifference.ts","../src/index.ts"],"sourcesContent":["/**\n * Exhaustiveness guard for discriminated unions. Place in the `default` branch of a `switch` over a\n * union's discriminant: if a new variant is added without a matching case, the call fails to compile.\n * Throws at runtime if reached with a value the types said was impossible.\n */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);\n}\n","import { readdir } from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport type { ILogger } from '@seedcord/types';\nimport type * as fs from 'node:fs';\n\n/**\n * Determines if a directory entry is a TypeScript or JavaScript file.\n *\n * @param entry - The directory entry to check.\n * @returns True if the entry is a file ending with .ts or .js.\n */\nexport function isTsOrJsFile(entry: fs.Dirent): boolean {\n return (\n entry.isFile() &&\n (entry.name.endsWith('.ts') || entry.name.endsWith('.js')) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.endsWith('.map')\n );\n}\n\n/**\n * Recursively traverses through a directory, importing all .ts and .js files and applying a callback to each import.\n *\n * @param dir - The directory path to traverse.\n * @param callback - A function that will be called for each imported module. It receives the full file path, the file's relative path, and the imported module as arguments.\n * @returns A Promise that resolves when the traversal is complete.\n */\nexport async function traverseDirectory(\n dir: string,\n callback: (fullPath: string, relativePath: string, imported: Record<string, unknown>) => Promise<void> | void,\n logger: ILogger\n): Promise<void> {\n let entries: fs.Dirent[];\n\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch (err) {\n logger.error(`Failed to read directory ${dir}`, err);\n entries = [];\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(process.cwd(), fullPath);\n\n if (entry.isDirectory()) {\n await traverseDirectory(fullPath, callback, logger);\n } else if (isTsOrJsFile(entry)) {\n const imported = (await import(fullPath)) as Record<string, unknown>;\n await callback(fullPath, relativePath, imported);\n }\n }\n}\n\n/**\n * Options for formatting file paths.\n */\nexport interface FormatFileOptions {\n /**\n * Whether to return only the directory part of the path. {@default false}\n */\n onlyDir?: boolean;\n /**\n * A prefix to prepend to the formatted path. {@default ./}\n */\n prefix?: string;\n}\n\n/**\n * Formats a file path relative to the current working directory.\n * @param filePath - The file path to format.\n * @param options - Formatting options.\n * @returns The formatted file path.\n */\nexport function formatFilePath(filePath: string, options: FormatFileOptions = {}): string {\n const { onlyDir = false, prefix = './' } = options;\n\n const resolved = onlyDir\n ? path.relative(process.cwd(), filePath.replace(/\\/[^/]*$/, ''))\n : path.relative(process.cwd(), filePath);\n return `${prefix}${resolved}`;\n}\n","/**\n * Shuffles an array using the Fisher-Yates algorithm.\n * This function creates a new array with the same elements in a random order,\n * without modifying the original array.\n *\n * @typeParam TArray - The type of elements in the array\n * @param items - The array to shuffle\n * @returns A new array with the same elements in a random order\n *\n * @example\n * ```typescript\n * const numbers = [1, 2, 3, 4, 5];\n * const shuffled = fyShuffle(numbers);\n * // shuffled might be [3, 1, 5, 2, 4]\n * // numbers is still [1, 2, 3, 4, 5]\n * ```\n */\nexport function fyShuffle<TArray>(items: TArray[]): TArray[] {\n const array = items.slice();\n for (let i = array.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n // @ts-expect-error - TypeScript doesn't recognize that TArray can be swapped\n [array[i], array[j]] = [array[j], array[i]];\n }\n return array;\n}\n","/**\n * Return current time in seconds\n */\nexport function currentTime(): number {\n return Math.floor(Date.now() / 1000);\n}\n","/**\n * Generates a random numeric code with the specified number of digits.\n *\n * @param digits - The number of digits for the generated code.\n * @returns A random numeric code with the specified number of digits.\n */\nexport function generateCode(digits: number): number {\n const min = Math.pow(10, digits - 1);\n const max = Math.pow(10, digits) - 1;\n return Math.floor(Math.random() * (max - min + 1) + min);\n}\n","/**\n * Returns the ordinal suffix for a given number.\n *\n * @param n - The number to get the ordinal for\n * @returns The number with its ordinal suffix\n *\n * @example\n * ordinal(1); // \"1st\"\n * ordinal(22); // \"22nd\"\n * ordinal(13); // \"13th\"\n */\nexport function ordinal(n: number): string {\n const s = ['th', 'st', 'nd', 'rd'];\n const v = n % 100;\n const index = (v - 20) % 10;\n const suffix = s[index] ?? s[v] ?? s[0];\n if (!suffix) return `${n}th`;\n\n return `${n}${suffix}`;\n}\n","/**\n * Takes two numbers and returns the percentage of the first number in the second number with two decimal places.\n *\n * @param num1 - The first number.\n * @param num2 - The second number.\n *\n * @returns The percentage of the first number in the second number with two decimal places.\n */\nexport function percentage(num1: number, num2: number): number {\n return Number(((num1 / num2) * 100).toFixed(2));\n}\n","/**\n * Rounds a number to a specified number of decimal places.\n *\n * @param num - The number to be rounded.\n * @param precision - The number of decimal places to round to.\n * @returns The rounded number.\n */\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round((num + Number.EPSILON) * factor) / factor;\n}\n","import type { TupleOf } from 'type-fest';\n\nexport interface RoundToDenomOptions {\n /**\n * Suffixes to use for each denomination level. Defaults to `['K', 'M', 'B', 'T', 'Q']`.\n */\n suffixes?: TupleOf<5, string>;\n /** Number of decimal places to include in the rounded result. Defaults to `1`. */\n precision?: number;\n}\n\n/**\n * Rounds a number to a string representation with a denomination suffix.\n * @param num - The number to round.\n * @example\n * ```ts\n * roundToDenomination(1234); // \"1.2K\"\n * roundToDenomination(10000, { suffixes: ['k', 'm', 'b', 't', 'q'] }); // \"10k\"\n * roundToDenomination(12345678); // \"12.3M\"\n * ```\n * @returns The rounded number as a string with a denomination suffix.\n */\nexport function roundToDenomination(num: number, opts?: RoundToDenomOptions): string {\n const { suffixes = ['K', 'M', 'B', 'T', 'Q'], precision = 1 } = opts ?? {};\n\n if (num < 10000) {\n return num.toString();\n }\n\n let index = -1;\n let temp = num;\n\n while (temp >= 1000 && index < suffixes.length - 1) {\n temp /= 1000;\n index++;\n }\n\n let result;\n\n if (temp % 1 === 0) {\n result = temp.toString();\n } else {\n const adjustedTemp = Math.round(temp * Math.pow(10, precision + 1)) / Math.pow(10, precision + 1);\n result = adjustedTemp.toFixed(precision);\n }\n\n if (result.endsWith('.9')) {\n result = Math.ceil(Number(result)).toString();\n }\n\n if (result.endsWith('.0')) {\n result = result.substring(0, result.length - 2);\n }\n\n if (result === '1000') {\n index += 1;\n result = '1';\n }\n\n return result + (index >= 0 ? suffixes[index] : '');\n}\n","import type { ILogger } from '@seedcord/types';\nimport type { JsonPrimitive } from 'type-fest';\n\n/**\n * JSONify an arbitrary type while allowing any object position to be replaced\n * by a circular marker. Optional keys stay optional.\n */\nexport type JsonifyWithCirculars<BaseType, Marker extends string = '[Circular]'> = BaseType extends JsonPrimitive\n ? BaseType\n : BaseType extends bigint\n ? string\n : BaseType extends Date\n ? string\n : BaseType extends { toJSON(): infer J }\n ? unknown extends J\n ? JsonifyObject<BaseType, Marker>\n : JsonifyWithCirculars<J, Marker>\n : BaseType extends readonly (infer U)[]\n ? (JsonifyWithCirculars<U, Marker> | Marker)[]\n : BaseType extends Map<infer K, infer V>\n ? [JsonifyWithCirculars<K, Marker> | Marker, JsonifyWithCirculars<V, Marker> | Marker][]\n : BaseType extends Set<infer U2>\n ? (JsonifyWithCirculars<U2, Marker> | Marker)[]\n : BaseType extends (...args: unknown[]) => unknown\n ? never\n : BaseType extends object\n ? JsonifyObject<BaseType, Marker>\n : never;\n\n/**\n * Helper to JSONify object types with circular markers.\n *\n * @internal\n */\nexport type JsonifyObject<BaseType, Marker extends string> = {\n [K in keyof BaseType as K extends symbol\n ? never\n : BaseType[K] extends (...args: unknown[]) => unknown\n ? never\n : K]:\n | JsonifyWithCirculars<Exclude<BaseType[K], undefined>, Marker>\n | Extract<BaseType[K], undefined>\n | Marker;\n};\n\n/**\n * Returned by {@link filterCirculars} when a value cannot be made JSON-safe. The original value is\n * never returned on failure, because it would re-throw in the caller's own `JSON.stringify`.\n */\nexport interface UnserializableValue {\n '[unserializable]': string;\n}\n\n/**\n * Configuration for {@link filterCirculars}.\n */\nexport interface FilterCircularsOptions<Marker extends string = '[Circular]'> {\n /** Optional {@link ILogger} used to log stringify or parse errors. */\n logger?: ILogger;\n /** Override the circular placeholder. Default is `[Circular]`. */\n marker?: Marker;\n /** Processing mode. `json` uses stringify and parse (might end up using a `toJSON()` if found). `decycle` builds a safe clone first. Default is `decycle`. */\n mode?: 'json' | 'decycle';\n}\n\n/**\n * Creates a clean, JSON safe copy of a value and replaces circular references with a marker.\n *\n * In `json` mode it behaves like stringify then parse with a replacer that handles cycles and BigInt.\n * In `decycle` mode it first clones without using toJSON, then you can stringify the result later.\n *\n * @typeParam ObjType - Type of the input value.\n * @typeParam Marker - Marker string used for circular references.\n *\n * @param value - The value to clone safely.\n * @param options - Optional configuration.\n *\n * @returns A JSON safe structure with circular references replaced by the marker.\n *\n * @example\n * ```ts\n * interface Test {\n * name: string;\n * self?: Test;\n * }\n *\n * const obj: Test = { name: 'seedcord' };\n * obj.self = obj;\n *\n * const clean = filterCirculars(obj);\n * // ^? { name: string; self?: \"[Circular]\" | { ... } }\n * console.log(clean.self); // \"[Circular]\"\n * ```\n */\nexport function filterCirculars<ObjType, Marker extends string = '[Circular]'>(\n value: ObjType,\n options?: FilterCircularsOptions<Marker>\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const logger = options?.logger;\n const marker = (options?.marker ?? '[Circular]') as Marker;\n const mode = options?.mode ?? 'decycle';\n\n if (mode === 'json') return json(value, marker, logger);\n\n try {\n return decycle(value, marker);\n } catch (error) {\n logger?.error('filterCirculars decycle error', error);\n return { '[unserializable]': 'decycle failed' };\n }\n}\n\n/**\n * Attempts to build a JSONified object using JSON.stringify and JSON.parse.\n *\n * @internal\n */\nfunction json<ObjType, Marker extends string>(\n value: ObjType,\n marker: Marker,\n logger?: ILogger\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const seen = new WeakSet<object>();\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value, (_k: string, v: unknown) => {\n if (typeof v === 'bigint') return v.toString();\n if (typeof v === 'object' && v !== null) {\n const obj = v;\n if (seen.has(obj)) return marker;\n seen.add(obj);\n }\n return v;\n });\n } catch (error) {\n logger?.error('filterCirculars stringify error', error);\n if (typeof value === 'object' && value !== null) {\n logger?.error('top level keys', Object.keys(value));\n }\n return { '[unserializable]': 'stringify failed' };\n }\n\n if (typeof json !== 'string') {\n return { '[unserializable]': 'stringify returned undefined' };\n }\n\n try {\n return JSON.parse(json) as JsonifyWithCirculars<ObjType, Marker>;\n } catch (error) {\n logger?.error('filterCirculars parse error', error);\n logger?.error('bad JSON', json);\n return { '[unserializable]': 'parse failed' };\n }\n}\n\n/**\n * Builds a JSON safe clone without calling toJSON on class instances.\n *\n * @internal\n */\nfunction decycle<ObjType, Marker extends string = '[Circular]'>(\n input: ObjType,\n marker: Marker,\n seen = new WeakSet<object>()\n): JsonifyWithCirculars<ObjType, Marker> {\n const recur = (val: unknown): unknown => {\n if (val === null) return null;\n const t = typeof val;\n\n if (t === 'bigint') return (val as bigint).toString();\n if (t !== 'object') return val;\n\n const obj = val as Record<string | number | symbol, unknown>;\n\n if (seen.has(obj)) return marker;\n seen.add(obj);\n\n if (obj instanceof Date) return obj.toISOString();\n if (obj instanceof RegExp) return obj.toString();\n\n if (Array.isArray(obj)) {\n return obj.map((item) => recur(item));\n }\n\n if (obj instanceof Map) {\n return Array.from(obj, ([k, v]) => [recur(k), recur(v)]);\n }\n\n if (obj instanceof Set) {\n return Array.from(obj, (v) => recur(v));\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (typeof v === 'function') continue;\n out[k] = recur(v);\n }\n\n return out;\n };\n\n return recur(input) as JsonifyWithCirculars<ObjType, Marker>;\n}\n","import type { UnionToIntersection } from 'type-fest';\n\n/**\n * Extracts the type of a nested property, distributing over unions.\n *\n * @internal\n */\nexport type DeepGet<Obj, Key extends string> = Obj extends unknown\n ? Key extends `${infer K}.${infer Rest}`\n ? K extends keyof Obj\n ? DeepGet<NonNullable<Obj[K]>, Rest>\n : never\n : Key extends keyof Obj\n ? Obj[Key]\n : never\n : never;\n\n/**\n * Converts a dot-notation path string into a nested object type with a specific leaf value.\n *\n * @internal\n */\nexport type PathToObj<Path extends string, Value> = Path extends `${infer Head}.${infer Tail}`\n ? { [K in Head]: PathToObj<Tail, Value> }\n : { [K in Path]: Value };\n\n/**\n * Checks for the presence of nested keys in an object that's possibly a distributed union, narrowing the object type accordingly.\n *\n * Checks if an object has the specified nested keys and that their values are not null or undefined. If they are not, the object type is narrowed to reflect the presence of these keys with their respective types from the original distributed union object.\n *\n * @param obj - The object to check.\n * @param keys - An array of dot-notation paths to check.\n * @returns True if all keys exist and are non-null/defined, narrowing the object type.\n *\n * @example\n * ```ts\n * interface Test {\n * a?: {\n * b?: {\n * c?: string;\n * };\n * } | null;\n * x?: number | null;\n * }\n *\n * const obj: Test = { a: { b: { c: 'hello' } }, x: 42 };\n *\n * if (hasKeys(obj, ['a.b.c', 'x'])) {\n * // Here, obj is narrowed to:\n * // {\n * // a: { b: { c: string } };\n * // x: number;\n * // }\n * console.log(obj.a.b.c.toUpperCase()); // Safe to access and use\n * console.log(obj.x.toFixed(2)); // Safe to access and use\n * }\n * ```\n */\nexport function hasKeys<Obj extends object, Keys extends string>(\n obj: Obj,\n keys: Keys[]\n): obj is Obj &\n UnionToIntersection<\n Keys extends unknown ? PathToObj<Keys, UnionToIntersection<NonNullable<DeepGet<Obj, Keys>>>> : never\n > {\n return keys.every((key) => {\n const parts = key.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (\n current === null ||\n current === undefined ||\n (typeof current !== 'object' && typeof current !== 'function')\n ) {\n return false;\n }\n if (!(part in current)) {\n return false;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current !== null && current !== undefined;\n });\n}\n","/**\n * Copies only the keys whose values are defined.\n *\n * @typeParam TObject - the original object type you're pulling from\n * @typeParam TKey - the keys to copy when defined\n * @param source - the object to read values from\n * @param keys - optional list of keys to include when present. If omitted, all keys are considered\n *\n * @example\n * ```ts\n * interface Config {\n * host?: string;\n * port?: number;\n * user?: string;\n * password?: string;\n * }\n *\n * const config: Config = {\n * host: 'localhost',\n * port: undefined,\n * user: 'admin',\n * password: undefined\n * };\n *\n * const definedConfig = keepDefined(config, 'host', 'port', 'user', 'password');\n * // Result: { host: 'localhost', user: 'admin' }\n * ```\n */\nexport function keepDefined<TObject extends object, TKey extends keyof TObject>(\n source: TObject,\n ...keys: readonly TKey[]\n): Partial<Pick<TObject, TKey extends never ? keyof TObject : TKey>> {\n const selectedKeys = keys.length > 0 ? keys : (Object.keys(source) as TKey[]);\n const result: Partial<TObject> = {};\n\n for (const key of selectedKeys) {\n const value = source[key];\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n}\n","/**\n * Returns the word with its first letter capitalized and the rest in lowercase.\n * @param word - The word to be formatted.\n * @returns The formatted word.\n */\nexport function capitalize(word: string): string {\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();\n}\n","/**\n * Function takes an array of strings or numbers and returns the number of characters in the longest string/number\n *\n * @param arr - The array of strings or numbers\n * @returns The length of the longest element when converted to string\n */\nexport function longestStringLength(arr: (string | number)[]): number {\n return Math.max(...arr.map((el) => el.toString().length));\n}\n","/**\n * Generates an ASCII table from the provided data.\n *\n * @param data - The data to be displayed in the table.\n * @returns The generated ASCII table as a string.\n */\nexport function generateAsciiTable(data: string[][]): string {\n if (data.length === 0) return '';\n\n const firstRow = data[0];\n if (!firstRow || firstRow.length === 0) return '';\n\n let table = '';\n const columnWidths: number[] = [];\n\n // Determine the maximum width for each column\n for (let i = 0; i < firstRow.length; i++) {\n let maxWidth = 0;\n for (const row of data) {\n const cell = row[i];\n if (cell !== undefined) maxWidth = Math.max(maxWidth, cell.length);\n }\n columnWidths.push(maxWidth);\n }\n\n // Function to create a horizontal line\n const createLine = (char: string, left: string, intersect: string, right: string): string => {\n let line = left;\n columnWidths.forEach((width, index) => {\n line += char.repeat(width + 2);\n if (index < columnWidths.length - 1) line += intersect;\n else line += right;\n });\n line += '\\n';\n return line;\n };\n\n // Top border\n table += createLine('═', '╔', '╦', '╗');\n\n data.forEach((row, rowIndex) => {\n // Row content\n table += '║';\n row.forEach((cell, columnIndex) => {\n const columnWidth = columnWidths[columnIndex];\n if (columnWidth !== undefined) table += ` ${cell.padEnd(columnWidth)} ║`;\n });\n table += '\\n';\n\n // Separator or bottom border\n if (rowIndex < data.length - 1) table += createLine('─', '╠', '╬', '╣');\n else table += createLine('═', '╚', '╩', '╝');\n });\n\n return table;\n}\n","import { capitalize } from './capitalize';\n\n/**\n * Options for the `prettify` function.\n */\nexport interface PrettifyOptions {\n capitalize?: boolean;\n}\n/**\n * Converts a string from any common naming convention to human-readable format.\n * Accepts camelCase, PascalCase, snake_case, and kebab-case input.\n *\n * @param key - The string to convert\n * @param opts - Optional configuration\n * @returns A space-separated, human-readable string\n *\n * @example\n * prettify(\"camelCaseString\") // \"camel Case String\"\n * prettify(\"PascalCaseString\") // \"Pascal Case String\"\n * prettify(\"snake_case_string\") // \"snake case string\"\n * prettify(\"kebab-case-string\") // \"kebab case string\"\n * prettify(\"mixedCase_string-name\") // \"mixed Case string name\"\n */\n\nexport function prettify(key: string, opts?: PrettifyOptions): string {\n const result = key\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase/PascalCase\n .replace(/[_-]/g, ' ') // snake_case and kebab-case\n .trim();\n\n if (opts?.capitalize) return capitalize(result);\n\n return result;\n}\n","/**\n * Calculates the difference between two numbers and formats it as a string with a '+' prefix for positive differences.\n *\n * @param numBefore - The initial number value\n * @param numAfter - The final number value\n * @returns A string representing the difference, with a '+' sign for positive differences\n *\n * @example\n * // Returns \"+5\"\n * prettyDifference(10, 15);\n *\n * @example\n * // Returns \"-3\"\n * prettyDifference(10, 7);\n */\nexport function prettyDifference(numBefore: number, numAfter: number): string {\n return (numAfter - numBefore > 0 ? `+${numAfter - numBefore}` : numAfter - numBefore).toString();\n}\n","export * from './misc';\nexport * from './numbers';\nexport * from './objects';\nexport * from './strings';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/misc/assertNever.ts","../src/misc/directory.ts","../src/misc/fyShuffle.ts","../src/numbers/toEpochSeconds.ts","../src/numbers/currentTime.ts","../src/numbers/generateCode.ts","../src/numbers/ordinal.ts","../src/numbers/parseDuration.ts","../src/numbers/percentage.ts","../src/numbers/round.ts","../src/numbers/roundToDenomination.ts","../src/objects/filterCirculars.ts","../src/objects/hasKeys.ts","../src/objects/keepDefined.ts","../src/strings/capitalize.ts","../src/strings/longestStringLength.ts","../src/strings/generateAsciiTable.ts","../src/strings/prettify.ts","../src/strings/prettyDifference.ts","../src/index.ts"],"sourcesContent":["/**\n * Exhaustiveness guard for discriminated unions. Place in the `default` branch of a `switch` over a\n * union's discriminant: if a new variant is added without a matching case, the call fails to compile.\n * Throws at runtime if reached with a value the types said was impossible.\n */\nexport function assertNever(value: never): never {\n throw new Error(`Unhandled discriminated union member: ${JSON.stringify(value)}`);\n}\n","import { readdir } from 'node:fs/promises';\nimport * as path from 'node:path';\n\nimport type { ILogger } from '@seedcord/types';\nimport type * as fs from 'node:fs';\n\n/**\n * Determines if a directory entry is a TypeScript or JavaScript file.\n *\n * @param entry - The directory entry to check.\n * @returns True if the entry is a file ending with .ts or .js.\n */\nexport function isTsOrJsFile(entry: fs.Dirent): boolean {\n return (\n entry.isFile() &&\n (entry.name.endsWith('.ts') || entry.name.endsWith('.js')) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.endsWith('.map')\n );\n}\n\n/**\n * Recursively traverses through a directory, importing all .ts and .js files and applying a callback to each import.\n *\n * @param dir - The directory path to traverse.\n * @param callback - A function that will be called for each imported module. It receives the full file path, the file's relative path, and the imported module as arguments.\n * @returns A Promise that resolves when the traversal is complete.\n */\nexport async function traverseDirectory(\n dir: string,\n callback: (fullPath: string, relativePath: string, imported: Record<string, unknown>) => Promise<void> | void,\n logger: ILogger\n): Promise<void> {\n let entries: fs.Dirent[];\n\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch (err) {\n logger.error(`Failed to read directory ${dir}`, err);\n entries = [];\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(process.cwd(), fullPath);\n\n if (entry.isDirectory()) {\n await traverseDirectory(fullPath, callback, logger);\n } else if (isTsOrJsFile(entry)) {\n const imported = (await import(fullPath)) as Record<string, unknown>;\n await callback(fullPath, relativePath, imported);\n }\n }\n}\n\n/**\n * Options for formatting file paths.\n */\nexport interface FormatFileOptions {\n /**\n * Whether to return only the directory part of the path.\n *\n * @defaultValue `false`\n */\n onlyDir?: boolean;\n /**\n * A prefix to prepend to the formatted path.\n *\n * @defaultValue `'./'`\n */\n prefix?: string;\n}\n\n/**\n * Formats a file path relative to the current working directory.\n * @param filePath - The file path to format.\n * @param options - Formatting options.\n * @returns The formatted file path.\n */\nexport function formatFilePath(filePath: string, options: FormatFileOptions = {}): string {\n const { onlyDir = false, prefix = './' } = options;\n\n const resolved = onlyDir\n ? path.relative(process.cwd(), filePath.replace(/\\/[^/]*$/, ''))\n : path.relative(process.cwd(), filePath);\n return `${prefix}${resolved}`;\n}\n","/**\n * Shuffles an array using the Fisher-Yates algorithm.\n * This function creates a new array with the same elements in a random order,\n * without modifying the original array.\n *\n * @typeParam TArray - The type of elements in the array\n * @param items - The array to shuffle\n * @returns A new array with the same elements in a random order\n *\n * @example\n * ```typescript\n * const numbers = [1, 2, 3, 4, 5];\n * const shuffled = fyShuffle(numbers);\n * // shuffled might be [3, 1, 5, 2, 4]\n * // numbers is still [1, 2, 3, 4, 5]\n * ```\n */\nexport function fyShuffle<TArray>(items: TArray[]): TArray[] {\n const array = items.slice();\n for (let i = array.length - 1; i > 0; i--) {\n const j = Math.floor(Math.random() * (i + 1));\n // @ts-expect-error - TypeScript doesn't recognize that TArray can be swapped\n [array[i], array[j]] = [array[j], array[i]];\n }\n return array;\n}\n","import type { EpochMs, EpochSec } from '@seedcord/types';\n\n/** Converts absolute epoch milliseconds to epoch seconds, the unit Discord `<t:...>` timestamp markup reads. */\nexport function toEpochSeconds(ms: EpochMs): EpochSec {\n return Math.round(ms / 1000) as EpochSec;\n}\n","import { toEpochSeconds } from './toEpochSeconds';\n\nimport type { EpochMs, EpochSec } from '@seedcord/types';\n\n/** Current time in epoch seconds. Shares the one ms→s rounding rule with {@link toEpochSeconds}. */\nexport function currentTime(): EpochSec {\n return toEpochSeconds(Date.now() as EpochMs);\n}\n","/**\n * Generates a random numeric code with the specified number of digits.\n *\n * @param digits - The number of digits for the generated code.\n * @returns A random numeric code with the specified number of digits.\n */\nexport function generateCode(digits: number): number {\n const min = Math.pow(10, digits - 1);\n const max = Math.pow(10, digits) - 1;\n return Math.floor(Math.random() * (max - min + 1) + min);\n}\n","/**\n * Returns the ordinal suffix for a given number.\n *\n * @param n - The number to get the ordinal for\n * @returns The number with its ordinal suffix\n *\n * @example\n * ordinal(1); // \"1st\"\n * ordinal(22); // \"22nd\"\n * ordinal(13); // \"13th\"\n */\nexport function ordinal(n: number): string {\n const s = ['th', 'st', 'nd', 'rd'];\n const v = n % 100;\n const index = (v - 20) % 10;\n const suffix = s[index] ?? s[v] ?? s[0];\n if (!suffix) return `${n}th`;\n\n return `${n}${suffix}`;\n}\n","const UNIT_MS = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000\n} as const;\n\ntype DurationUnit = keyof typeof UNIT_MS;\n\n/** A duration literal like `30m`, `24h`, or `500ms`, a number followed by a unit (`ms`, `s`, `m`, `h`, `d`). */\nexport type ValidDuration = `${number}${DurationUnit}`;\n\nconst DURATION_PATTERN = new RegExp(`^(\\\\d+)(${Object.keys(UNIT_MS).join('|')})$`);\n\n/**\n * Parses a short duration string like `24h`, `30m`, `90s`, `7d`, or `500ms` into milliseconds.\n *\n * The grammar is one or more digits followed by one lowercase unit (`ms`, `s`, `m`, `h`, `d`).\n * Anything else returns `null`, including a bare number, an unknown unit, an uppercase unit,\n * surrounding whitespace, a fractional value, and a result of zero. The return is a positive\n * number or `null`, never `0` or `NaN`, so a malformed input can never read as a valid duration.\n *\n * @param input - The duration string to parse.\n * @returns The duration in milliseconds, or `null` if `input` is not a well-formed positive duration.\n */\nexport function parseDuration(input: string): number | null {\n const match = DURATION_PATTERN.exec(input);\n if (!match) return null;\n\n const ms = Number(match[1]) * UNIT_MS[match[2] as DurationUnit];\n return ms > 0 ? ms : null;\n}\n","/**\n * Takes two numbers and returns the percentage of the first number in the second number with two decimal places.\n *\n * @param num1 - The first number.\n * @param num2 - The second number.\n *\n * @returns The percentage of the first number in the second number with two decimal places.\n */\nexport function percentage(num1: number, num2: number): number {\n return Number(((num1 / num2) * 100).toFixed(2));\n}\n","/**\n * Rounds a number to a specified number of decimal places.\n *\n * @param num - The number to be rounded.\n * @param precision - The number of decimal places to round to.\n * @returns The rounded number.\n */\nexport function round(num: number, precision: number): number {\n const factor = Math.pow(10, precision);\n return Math.round((num + Number.EPSILON) * factor) / factor;\n}\n","import type { TupleOf } from 'type-fest';\n\nexport interface RoundToDenomOptions {\n /**\n * Suffixes to use for each denomination level.\n *\n * @defaultValue `['K', 'M', 'B', 'T', 'Q']`\n */\n suffixes?: TupleOf<5, string>;\n /**\n * Number of decimal places to include in the rounded result.\n *\n * @defaultValue `1`\n */\n precision?: number;\n}\n\n/**\n * Rounds a number to a string representation with a denomination suffix.\n * @param num - The number to round.\n * @example\n * ```ts\n * roundToDenomination(1234); // \"1.2K\"\n * roundToDenomination(10000, { suffixes: ['k', 'm', 'b', 't', 'q'] }); // \"10k\"\n * roundToDenomination(12345678); // \"12.3M\"\n * ```\n * @returns The rounded number as a string with a denomination suffix.\n */\nexport function roundToDenomination(num: number, opts?: RoundToDenomOptions): string {\n const { suffixes = ['K', 'M', 'B', 'T', 'Q'], precision = 1 } = opts ?? {};\n\n if (num < 10000) {\n return num.toString();\n }\n\n let index = -1;\n let temp = num;\n\n while (temp >= 1000 && index < suffixes.length - 1) {\n temp /= 1000;\n index++;\n }\n\n let result;\n\n if (temp % 1 === 0) {\n result = temp.toString();\n } else {\n const adjustedTemp = Math.round(temp * Math.pow(10, precision + 1)) / Math.pow(10, precision + 1);\n result = adjustedTemp.toFixed(precision);\n }\n\n if (result.endsWith('.9')) {\n result = Math.ceil(Number(result)).toString();\n }\n\n if (result.endsWith('.0')) {\n result = result.substring(0, result.length - 2);\n }\n\n if (result === '1000') {\n index += 1;\n result = '1';\n }\n\n return result + (index >= 0 ? suffixes[index] : '');\n}\n","import type { ILogger } from '@seedcord/types';\nimport type { JsonPrimitive } from 'type-fest';\n\n/**\n * JSONify an arbitrary type while allowing any object position to be replaced\n * by a circular marker. Optional keys stay optional.\n */\nexport type JsonifyWithCirculars<BaseType, Marker extends string = '[Circular]'> = BaseType extends JsonPrimitive\n ? BaseType\n : BaseType extends bigint\n ? string\n : BaseType extends Date\n ? string\n : BaseType extends { toJSON(): infer J }\n ? unknown extends J\n ? JsonifyObject<BaseType, Marker>\n : JsonifyWithCirculars<J, Marker>\n : BaseType extends readonly (infer U)[]\n ? (JsonifyWithCirculars<U, Marker> | Marker)[]\n : BaseType extends Map<infer K, infer V>\n ? [JsonifyWithCirculars<K, Marker> | Marker, JsonifyWithCirculars<V, Marker> | Marker][]\n : BaseType extends Set<infer U2>\n ? (JsonifyWithCirculars<U2, Marker> | Marker)[]\n : BaseType extends (...args: unknown[]) => unknown\n ? never\n : BaseType extends object\n ? JsonifyObject<BaseType, Marker>\n : never;\n\n/**\n * Helper to JSONify object types with circular markers.\n *\n * @internal\n */\nexport type JsonifyObject<BaseType, Marker extends string> = {\n [K in keyof BaseType as K extends symbol\n ? never\n : BaseType[K] extends (...args: unknown[]) => unknown\n ? never\n : K]:\n | JsonifyWithCirculars<Exclude<BaseType[K], undefined>, Marker>\n | Extract<BaseType[K], undefined>\n | Marker;\n};\n\n/**\n * Returned by {@link filterCirculars} when a value cannot be made JSON-safe. The original value is\n * never returned on failure, because it would re-throw in the caller's own `JSON.stringify`.\n */\nexport interface UnserializableValue {\n '[unserializable]': string;\n}\n\n/**\n * Configuration for {@link filterCirculars}.\n */\nexport interface FilterCircularsOptions<Marker extends string = '[Circular]'> {\n /** Optional {@link ILogger} used to log stringify or parse errors. */\n logger?: ILogger;\n /**\n * Override the circular placeholder.\n *\n * @defaultValue `'[Circular]'`\n */\n marker?: Marker;\n /**\n * Processing mode. `json` uses stringify and parse (might end up using a `toJSON()` if found). `decycle` builds a safe clone first.\n *\n * @defaultValue `'decycle'`\n */\n mode?: 'json' | 'decycle';\n}\n\n/**\n * Creates a clean, JSON safe copy of a value and replaces circular references with a marker.\n *\n * In `json` mode it behaves like stringify then parse with a replacer that handles cycles and BigInt.\n * In `decycle` mode it first clones without using toJSON, then you can stringify the result later.\n *\n * @typeParam ObjType - Type of the input value.\n * @typeParam Marker - Marker string used for circular references.\n *\n * @param value - The value to clone safely.\n * @param options - Optional configuration.\n *\n * @returns A JSON safe structure with circular references replaced by the marker.\n *\n * @example\n * ```ts\n * interface Test {\n * name: string;\n * self?: Test;\n * }\n *\n * const obj: Test = { name: 'seedcord' };\n * obj.self = obj;\n *\n * const clean = filterCirculars(obj);\n * // ^? { name: string; self?: \"[Circular]\" | { ... } }\n * console.log(clean.self); // \"[Circular]\"\n * ```\n */\nexport function filterCirculars<ObjType, Marker extends string = '[Circular]'>(\n value: ObjType,\n options?: FilterCircularsOptions<Marker>\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const logger = options?.logger;\n const marker = (options?.marker ?? '[Circular]') as Marker;\n const mode = options?.mode ?? 'decycle';\n\n if (mode === 'json') return json(value, marker, logger);\n\n try {\n return decycle(value, marker);\n } catch (error) {\n logger?.error('filterCirculars decycle error', error);\n return { '[unserializable]': 'decycle failed' };\n }\n}\n\n/**\n * Attempts to build a JSONified object using JSON.stringify and JSON.parse.\n *\n * @internal\n */\nfunction json<ObjType, Marker extends string>(\n value: ObjType,\n marker: Marker,\n logger?: ILogger\n): JsonifyWithCirculars<ObjType, Marker> | UnserializableValue {\n const seen = new WeakSet<object>();\n let json: string | undefined;\n\n try {\n json = JSON.stringify(value, (_k: string, v: unknown) => {\n if (typeof v === 'bigint') return v.toString();\n if (typeof v === 'object' && v !== null) {\n const obj = v;\n if (seen.has(obj)) return marker;\n seen.add(obj);\n }\n return v;\n });\n } catch (error) {\n logger?.error('filterCirculars stringify error', error);\n if (typeof value === 'object' && value !== null) {\n logger?.error('top level keys', Object.keys(value));\n }\n return { '[unserializable]': 'stringify failed' };\n }\n\n if (typeof json !== 'string') {\n return { '[unserializable]': 'stringify returned undefined' };\n }\n\n try {\n return JSON.parse(json) as JsonifyWithCirculars<ObjType, Marker>;\n } catch (error) {\n logger?.error('filterCirculars parse error', error);\n logger?.error('bad JSON', json);\n return { '[unserializable]': 'parse failed' };\n }\n}\n\n/**\n * Builds a JSON safe clone without calling toJSON on class instances.\n *\n * @internal\n */\nfunction decycle<ObjType, Marker extends string = '[Circular]'>(\n input: ObjType,\n marker: Marker,\n seen = new WeakSet<object>()\n): JsonifyWithCirculars<ObjType, Marker> {\n const recur = (val: unknown): unknown => {\n if (val === null) return null;\n const t = typeof val;\n\n if (t === 'bigint') return (val as bigint).toString();\n if (t !== 'object') return val;\n\n const obj = val as Record<string | number | symbol, unknown>;\n\n if (seen.has(obj)) return marker;\n seen.add(obj);\n\n if (obj instanceof Date) return obj.toISOString();\n if (obj instanceof RegExp) return obj.toString();\n\n if (Array.isArray(obj)) {\n return obj.map((item) => recur(item));\n }\n\n if (obj instanceof Map) {\n return Array.from(obj, ([k, v]) => [recur(k), recur(v)]);\n }\n\n if (obj instanceof Set) {\n return Array.from(obj, (v) => recur(v));\n }\n\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (typeof v === 'function') continue;\n out[k] = recur(v);\n }\n\n return out;\n };\n\n return recur(input) as JsonifyWithCirculars<ObjType, Marker>;\n}\n","import type { UnionToIntersection } from 'type-fest';\n\n/**\n * Extracts the type of a nested property, distributing over unions.\n *\n * @internal\n */\nexport type DeepGet<Obj, Key extends string> = Obj extends unknown\n ? Key extends `${infer K}.${infer Rest}`\n ? K extends keyof Obj\n ? DeepGet<NonNullable<Obj[K]>, Rest>\n : never\n : Key extends keyof Obj\n ? Obj[Key]\n : never\n : never;\n\n/**\n * Converts a dot-notation path string into a nested object type with a specific leaf value.\n *\n * @internal\n */\nexport type PathToObj<Path extends string, Value> = Path extends `${infer Head}.${infer Tail}`\n ? { [K in Head]: PathToObj<Tail, Value> }\n : { [K in Path]: Value };\n\n/**\n * Checks for the presence of nested keys in an object that's possibly a distributed union, narrowing the object type accordingly.\n *\n * Checks if an object has the specified nested keys and that their values are not null or undefined. If they are not, the object type is narrowed to reflect the presence of these keys with their respective types from the original distributed union object.\n *\n * @param obj - The object to check.\n * @param keys - An array of dot-notation paths to check.\n * @returns True if all keys exist and are non-null/defined, narrowing the object type.\n *\n * @example\n * ```ts\n * interface Test {\n * a?: {\n * b?: {\n * c?: string;\n * };\n * } | null;\n * x?: number | null;\n * }\n *\n * const obj: Test = { a: { b: { c: 'hello' } }, x: 42 };\n *\n * if (hasKeys(obj, ['a.b.c', 'x'])) {\n * // Here, obj is narrowed to:\n * // {\n * // a: { b: { c: string } };\n * // x: number;\n * // }\n * console.log(obj.a.b.c.toUpperCase()); // Safe to access and use\n * console.log(obj.x.toFixed(2)); // Safe to access and use\n * }\n * ```\n */\nexport function hasKeys<Obj extends object, Keys extends string>(\n obj: Obj,\n keys: Keys[]\n): obj is Obj &\n UnionToIntersection<\n Keys extends unknown ? PathToObj<Keys, UnionToIntersection<NonNullable<DeepGet<Obj, Keys>>>> : never\n > {\n return keys.every((key) => {\n const parts = key.split('.');\n let current: unknown = obj;\n\n for (const part of parts) {\n if (\n current === null ||\n current === undefined ||\n (typeof current !== 'object' && typeof current !== 'function')\n ) {\n return false;\n }\n if (!(part in current)) {\n return false;\n }\n current = (current as Record<string, unknown>)[part];\n }\n\n return current !== null && current !== undefined;\n });\n}\n","/**\n * Copies only the keys whose values are defined.\n *\n * @typeParam TObject - the original object type you're pulling from\n * @typeParam TKey - the keys to copy when defined\n * @param source - the object to read values from\n * @param keys - optional list of keys to include when present. {@default all keys}\n *\n * @example\n * ```ts\n * interface Config {\n * host?: string;\n * port?: number;\n * user?: string;\n * password?: string;\n * }\n *\n * const config: Config = {\n * host: 'localhost',\n * port: undefined,\n * user: 'admin',\n * password: undefined\n * };\n *\n * const definedConfig = keepDefined(config, 'host', 'port', 'user', 'password');\n * // Result: { host: 'localhost', user: 'admin' }\n * ```\n */\nexport function keepDefined<TObject extends object, TKey extends keyof TObject>(\n source: TObject,\n ...keys: readonly TKey[]\n): Partial<Pick<TObject, TKey extends never ? keyof TObject : TKey>> {\n const selectedKeys = keys.length > 0 ? keys : (Object.keys(source) as TKey[]);\n const result: Partial<TObject> = {};\n\n for (const key of selectedKeys) {\n const value = source[key];\n if (value !== undefined && value !== null) {\n result[key] = value;\n }\n }\n return result;\n}\n","/**\n * Returns the word with its first letter capitalized and the rest in lowercase.\n * @param word - The word to be formatted.\n * @returns The formatted word.\n */\nexport function capitalize(word: string): string {\n return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();\n}\n","/**\n * Function takes an array of strings or numbers and returns the number of characters in the longest string/number\n *\n * @param arr - The array of strings or numbers\n * @returns The length of the longest element when converted to string\n */\nexport function longestStringLength(arr: (string | number)[]): number {\n return Math.max(...arr.map((el) => el.toString().length));\n}\n","/**\n * Generates an ASCII table from the provided data.\n *\n * @param data - The data to be displayed in the table.\n * @returns The generated ASCII table as a string.\n */\nexport function generateAsciiTable(data: string[][]): string {\n if (data.length === 0) return '';\n\n const firstRow = data[0];\n if (!firstRow || firstRow.length === 0) return '';\n\n let table = '';\n const columnWidths: number[] = [];\n\n // Determine the maximum width for each column\n for (let i = 0; i < firstRow.length; i++) {\n let maxWidth = 0;\n for (const row of data) {\n const cell = row[i];\n if (cell !== undefined) maxWidth = Math.max(maxWidth, cell.length);\n }\n columnWidths.push(maxWidth);\n }\n\n // Function to create a horizontal line\n const createLine = (char: string, left: string, intersect: string, right: string): string => {\n let line = left;\n columnWidths.forEach((width, index) => {\n line += char.repeat(width + 2);\n if (index < columnWidths.length - 1) line += intersect;\n else line += right;\n });\n line += '\\n';\n return line;\n };\n\n // Top border\n table += createLine('═', '╔', '╦', '╗');\n\n data.forEach((row, rowIndex) => {\n // Row content\n table += '║';\n row.forEach((cell, columnIndex) => {\n const columnWidth = columnWidths[columnIndex];\n if (columnWidth !== undefined) table += ` ${cell.padEnd(columnWidth)} ║`;\n });\n table += '\\n';\n\n // Separator or bottom border\n if (rowIndex < data.length - 1) table += createLine('─', '╠', '╬', '╣');\n else table += createLine('═', '╚', '╩', '╝');\n });\n\n return table;\n}\n","import { capitalize } from './capitalize';\n\n/**\n * Options for the `prettify` function.\n */\nexport interface PrettifyOptions {\n capitalize?: boolean;\n}\n/**\n * Converts a string from any common naming convention to human-readable format.\n * Accepts camelCase, PascalCase, snake_case, and kebab-case input.\n *\n * @param key - The string to convert\n * @param opts - Optional configuration\n * @returns A space-separated, human-readable string\n *\n * @example\n * prettify(\"camelCaseString\") // \"camel Case String\"\n * prettify(\"PascalCaseString\") // \"Pascal Case String\"\n * prettify(\"snake_case_string\") // \"snake case string\"\n * prettify(\"kebab-case-string\") // \"kebab case string\"\n * prettify(\"mixedCase_string-name\") // \"mixed Case string name\"\n */\n\nexport function prettify(key: string, opts?: PrettifyOptions): string {\n const result = key\n .replace(/([a-z])([A-Z])/g, '$1 $2') // camelCase/PascalCase\n .replace(/[_-]/g, ' ') // snake_case and kebab-case\n .trim();\n\n if (opts?.capitalize) return capitalize(result);\n\n return result;\n}\n","/**\n * Calculates the difference between two numbers and formats it as a string with a '+' prefix for positive differences.\n *\n * @param numBefore - The initial number value\n * @param numAfter - The final number value\n * @returns A string representing the difference, with a '+' sign for positive differences\n *\n * @example\n * // Returns \"+5\"\n * prettyDifference(10, 15);\n *\n * @example\n * // Returns \"-3\"\n * prettyDifference(10, 7);\n */\nexport function prettyDifference(numBefore: number, numAfter: number): string {\n return (numAfter - numBefore > 0 ? `+${numAfter - numBefore}` : numAfter - numBefore).toString();\n}\n","export * from './misc';\nexport * from './numbers';\nexport * from './objects';\nexport * from './strings';\n\n/** Package version */\nexport const version = process.env.PACKAGE_VERSION ?? '0.0.0';\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seedcord/utils",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.0-next.0",
|
|
5
5
|
"description": "Utility functions for Seedcord packages",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -41,13 +41,13 @@
|
|
|
41
41
|
"typescript": "^6.0.3"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"discord-api-types": "^0.38.
|
|
45
|
-
"@seedcord/types": "0.
|
|
44
|
+
"discord-api-types": "^0.38.48",
|
|
45
|
+
"@seedcord/types": "0.6.0-next.0"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"discord.js": "^14.26.4",
|
|
49
|
-
"type-fest": "^5.
|
|
50
|
-
"@seedcord/eslint-config": "1.4.0",
|
|
49
|
+
"type-fest": "^5.7.0",
|
|
50
|
+
"@seedcord/eslint-config": "1.4.1-next.0",
|
|
51
51
|
"@seedcord/tsconfig": "2.0.0",
|
|
52
52
|
"@seedcord/tsdown-config": "2.0.0"
|
|
53
53
|
},
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"test:watch": "vitest dev",
|
|
68
68
|
"coverage": "vitest run --coverage"
|
|
69
69
|
},
|
|
70
|
-
"readme": "<p align=\"center\">\n <img src=\"https://
|
|
70
|
+
"readme": "<p align=\"center\">\n <img src=\"https://cdn.seedcord.org/assets/banner.webp\" alt=\"seedcord\" width=\"100%\" />\n</p>\n\n---\n\n_This repository is a work in progress._\n\n- There are no stable releases yet but changes are being made actively.\n- Till a major v1.0.0 release for seedcord, expect breaking changes in minor versions.\n- Documentation will come soon as well!\n\nIf you'd like to try it out, you can check out the code in `mock`\n"
|
|
71
71
|
}
|