@sorrell/utilities 1.2.15 → 1.2.16

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.
Files changed (60) hide show
  1. package/Distribution/PackageExports.Generated.json +10 -0
  2. package/Distribution/Types/FileSystem/FileSystem.d.cts +2 -0
  3. package/Distribution/Types/FileSystem/FileSystem.d.cts.map +1 -1
  4. package/Distribution/Types/FileSystem/FileSystem.d.mts +2 -0
  5. package/Distribution/Types/FileSystem/FileSystem.d.mts.map +1 -1
  6. package/Distribution/Types/FileSystem/FileSystem.d.ts +2 -0
  7. package/Distribution/Types/FileSystem/FileSystem.d.ts.map +1 -1
  8. package/Distribution/Types/FileSystem/Module/Module.Internal.d.cts +7 -0
  9. package/Distribution/Types/FileSystem/Module/Module.Internal.d.cts.map +10 -0
  10. package/Distribution/Types/FileSystem/Module/Module.Internal.d.mts +7 -0
  11. package/Distribution/Types/FileSystem/Module/Module.Internal.d.mts.map +10 -0
  12. package/Distribution/Types/FileSystem/Module/Module.Internal.d.ts +7 -0
  13. package/Distribution/Types/FileSystem/Module/Module.Internal.d.ts.map +1 -0
  14. package/Distribution/Types/FileSystem/Module/Module.Types.d.cts +14 -0
  15. package/Distribution/Types/FileSystem/Module/Module.Types.d.cts.map +10 -0
  16. package/Distribution/Types/FileSystem/Module/Module.Types.d.mts +14 -0
  17. package/Distribution/Types/FileSystem/Module/Module.Types.d.mts.map +10 -0
  18. package/Distribution/Types/FileSystem/Module/Module.Types.d.ts +14 -0
  19. package/Distribution/Types/FileSystem/Module/Module.Types.d.ts.map +1 -0
  20. package/Distribution/Types/FileSystem/Module/Module.d.cts +15 -0
  21. package/Distribution/Types/FileSystem/Module/Module.d.cts.map +10 -0
  22. package/Distribution/Types/FileSystem/Module/Module.d.mts +15 -0
  23. package/Distribution/Types/FileSystem/Module/Module.d.mts.map +10 -0
  24. package/Distribution/Types/FileSystem/Module/Module.d.ts +15 -0
  25. package/Distribution/Types/FileSystem/Module/Module.d.ts.map +1 -0
  26. package/Distribution/Types/FileSystem/Module/index.d.cts +9 -0
  27. package/Distribution/Types/FileSystem/Module/index.d.cts.map +10 -0
  28. package/Distribution/Types/FileSystem/Module/index.d.mts +9 -0
  29. package/Distribution/Types/FileSystem/Module/index.d.mts.map +10 -0
  30. package/Distribution/Types/FileSystem/Module/index.d.ts +9 -0
  31. package/Distribution/Types/FileSystem/Module/index.d.ts.map +1 -0
  32. package/Distribution/Types/FileSystem/index.d.cts +1 -0
  33. package/Distribution/Types/FileSystem/index.d.cts.map +1 -1
  34. package/Distribution/Types/FileSystem/index.d.mts +1 -0
  35. package/Distribution/Types/FileSystem/index.d.mts.map +1 -1
  36. package/Distribution/Types/FileSystem/index.d.ts +1 -0
  37. package/Distribution/Types/FileSystem/index.d.ts.map +1 -1
  38. package/Distribution/Types/String/String.d.cts +1 -0
  39. package/Distribution/Types/String/String.d.cts.map +1 -1
  40. package/Distribution/Types/String/String.d.mts +1 -0
  41. package/Distribution/Types/String/String.d.mts.map +1 -1
  42. package/Distribution/Types/String/String.d.ts +1 -0
  43. package/Distribution/Types/String/String.d.ts.map +1 -1
  44. package/Distribution/fs-module.cjs +161 -0
  45. package/Distribution/fs-module.cjs.map +7 -0
  46. package/Distribution/fs-module.js +128 -0
  47. package/Distribution/fs-module.js.map +7 -0
  48. package/Distribution/fs.cjs +116 -2
  49. package/Distribution/fs.cjs.map +4 -4
  50. package/Distribution/fs.js +125 -5
  51. package/Distribution/fs.js.map +4 -4
  52. package/Distribution/index.cjs +144 -37
  53. package/Distribution/index.cjs.map +4 -4
  54. package/Distribution/index.js +147 -40
  55. package/Distribution/index.js.map +4 -4
  56. package/Distribution/string.cjs +4 -0
  57. package/Distribution/string.cjs.map +2 -2
  58. package/Distribution/string.js +4 -0
  59. package/Distribution/string.js.map +2 -2
  60. package/package.json +11 -1
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../Source/FileSystem/FileSystem.ts"],
4
- "sourcesContent": ["/**\n * @file FileSystem.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nimport { basename, dirname, extname, join } from \"path\";\nimport { type FFileExtension } from \"./FileSystem.Types.ts\";\nimport { type FileHandle } from \"fs/promises\";\nimport { promises as Fs } from \"fs\";\nimport { constants as FsConstants } from \"fs\";\nimport os from \"os\";\nasync function PathExists(Path: string): Promise<boolean> {\n try {\n await Fs.access(Path, FsConstants.F_OK);\n return true;\n }\n catch {\n return false;\n }\n}\n/**\n * @param Extension - The file extension that you wish to test.\n * @returns Whether the file extension is valid on the current platform.\n */\nexport function IsSupportedFileExtension(Extension: FFileExtension): boolean {\n let NormalizedExtension: string = Extension.slice(1);\n if (NormalizedExtension.startsWith(\".\")) {\n NormalizedExtension = NormalizedExtension.slice(1);\n }\n if (NormalizedExtension.length === 0) {\n return false;\n }\n if (NormalizedExtension.includes(\"/\")\n || NormalizedExtension.includes(\"\\\\\")\n || NormalizedExtension.includes(\"\\0\")) {\n return false;\n }\n if (os.platform() === \"win32\") {\n /* eslint-disable-next-line no-control-regex */\n const HasIllegalCharacters: boolean = /[<>:\"/\\\\|?*\\x00-\\x1F]/u.test(NormalizedExtension);\n if (HasIllegalCharacters) {\n return false;\n }\n if (/[ .]$/u.test(NormalizedExtension)) {\n return false;\n }\n }\n return true;\n}\n/**\n * Given a path at which we wish to create a new file, check if a file at that\n * path already exists, and if so, then append `(${ number })` before the file\n * extension, consistent with the Windows Explorer handles conflicting file\n * names when pasting files.\n */\nexport async function GetSafeNewPath(InPath: string): Promise<string> {\n const DirectoryPath: string = dirname(InPath);\n const Extension: string = extname(InPath);\n const FileName: string = basename(InPath);\n const BaseFileName: string = Extension === \"\"\n ? FileName\n : basename(InPath, Extension);\n let CandidatePath: string = InPath;\n let Index: number = 1;\n while (await PathExists(CandidatePath)) {\n const CandidateFileName: string = `${BaseFileName} (${Index})${Extension}`;\n CandidatePath = join(DirectoryPath, CandidateFileName);\n Index++;\n }\n return CandidatePath;\n}\n/**\n * @param DirectoryPath - The path to the directory in which you wish to check.\n * @param FileName - The desired file name.\n * @param PersistNewFile - *(Optional)* Whether to keep the otherwise-temporary\n * file created at the desired path.\n * @param Extension - *(Optional)* If provided, the function will only return `true`\n * if `FileName.endsWith(Extension)` *and* the `Extension` is a valid\n * file extension.\n * @returns Whether a file of the given `FileName` can be created in `DirectoryPath`.\n *\n * @remarks This *does* attempt to create a file at the desired path. The file is\n * temporary iff `!PersistNewFile`, and is never created when this function\n * returns `false`.\n */\nexport async function IsValidFileName(DirectoryPath: string, FileName: string, PersistNewFile: boolean = false, Extension: FFileExtension | undefined = undefined): Promise<boolean> {\n const ExtensionSafe: FFileExtension | null | \"\" = Extension !== undefined\n ? (Extension.startsWith(\".\") && IsSupportedFileExtension(Extension))\n ? Extension\n : null\n : \"\";\n const IsExtensionImproper: boolean = (ExtensionSafe === null ||\n ExtensionSafe === \".\" ||\n !FileName.endsWith(ExtensionSafe));\n if (IsExtensionImproper) {\n return false;\n }\n const FilePath: string = join(DirectoryPath, FileName);\n try {\n const ThisFileHandle: FileHandle = await Fs.open(FilePath, \"wx\");\n await ThisFileHandle.close();\n if (!PersistNewFile) {\n await Fs.unlink(FilePath);\n }\n return true;\n }\n catch {\n return false;\n }\n}\n/**\n * Write a text file to a given {@link Path} having contents {@link Contents}.\n *\n * @param Path - The path of the file that will be written.\n * @param Contents - The text contents of the file to write.\n *\n * @returns {Promise<void>} A {@link Promise} that resolves when the call to {@link Fs.writeFile} resolves.\n *\n * @example\n * ```typescript\n * import { WriteTextFile } from \"@sorrell/utilities/fs\";\n * import { resolve } from \"path\";\n *\n * const MyReadMe: string = \"# ReadMe\\n\\nThis package accomplishes...\\n\";\n * const MyReadMePath: string = resolve(\".\");\n *\n * await WriteTextFile(MyReadMePath, MyReadMe);\n * ```\n */\nexport async function WriteTextFile(Path: string, Contents: string): Promise<void> {\n await Fs.writeFile(Path, Contents, { encoding: \"utf-8\" });\n}\nimport * as FileSystem from \"node:fs/promises\";\nimport * as Path from \"node:path\";\nimport * as Readline from \"node:readline\";\ntype DeletePhase = \"Scanning\" | \"Deleting\" | \"Done\";\ntype DeleteEntryKind = \"File\" | \"Directory\" | \"Other\";\ntype DeleteEntry = {\n EntryPath: string;\n Kind: DeleteEntryKind;\n Size: number;\n};\ntype DeleteProgress = {\n Phase: DeletePhase;\n CurrentPath: string | null;\n DiscoveredEntries: number;\n TotalEntries: number;\n DeletedEntries: number;\n TotalBytes: number;\n DeletedBytes: number;\n};\ntype DeleteWithProgressOptions = {\n OnProgress?: (Progress: DeleteProgress) => void;\n Signal?: AbortSignal;\n};\nfunction IsMissingFileError(ErrorValue: unknown): boolean {\n return (typeof ErrorValue === \"object\" &&\n ErrorValue !== null &&\n \"code\" in ErrorValue &&\n ErrorValue.code === \"ENOENT\");\n}\nfunction CloneProgress(Progress: DeleteProgress): DeleteProgress {\n return { ...Progress };\n}\nasync function BuildDeletionPlan(RootPath: string, Progress: DeleteProgress, Options: DeleteWithProgressOptions): Promise<DeleteEntry[]> {\n const Entries: DeleteEntry[] = [];\n async function Visit(CurrentPath: string): Promise<void> {\n Options.Signal?.throwIfAborted();\n Progress.Phase = \"Scanning\";\n Progress.CurrentPath = CurrentPath;\n Options.OnProgress?.(CloneProgress(Progress));\n let Stats;\n try {\n Stats = await FileSystem.lstat(CurrentPath);\n }\n catch (ErrorValue) {\n if (IsMissingFileError(ErrorValue)) {\n return;\n }\n throw ErrorValue;\n }\n if (Stats.isDirectory()) {\n const Children = await FileSystem.readdir(CurrentPath, {\n withFileTypes: true\n });\n for (const Child of Children) {\n await Visit(Path.join(CurrentPath, Child.name));\n }\n Entries.push({\n EntryPath: CurrentPath,\n Kind: \"Directory\",\n Size: 0\n });\n }\n else {\n const Size = Stats.isFile() ? Stats.size : 0;\n Entries.push({\n EntryPath: CurrentPath,\n Kind: Stats.isFile() ? \"File\" : \"Other\",\n Size\n });\n Progress.TotalBytes += Size;\n }\n Progress.DiscoveredEntries = Entries.length;\n Options.OnProgress?.(CloneProgress(Progress));\n }\n await Visit(RootPath);\n return Entries;\n}\nexport async function DeleteWithProgress(RootPath: string, Options: DeleteWithProgressOptions = {}): Promise<void> {\n const Progress: DeleteProgress = {\n Phase: \"Scanning\",\n CurrentPath: null,\n DiscoveredEntries: 0,\n TotalEntries: 0,\n DeletedEntries: 0,\n TotalBytes: 0,\n DeletedBytes: 0\n };\n const Entries = await BuildDeletionPlan(RootPath, Progress, Options);\n Progress.Phase = \"Deleting\";\n Progress.TotalEntries = Entries.length;\n Progress.CurrentPath = null;\n Options.OnProgress?.(CloneProgress(Progress));\n for (const Entry of Entries) {\n Options.Signal?.throwIfAborted();\n Progress.CurrentPath = Entry.EntryPath;\n Options.OnProgress?.(CloneProgress(Progress));\n try {\n if (Entry.Kind === \"Directory\") {\n await FileSystem.rmdir(Entry.EntryPath);\n }\n else {\n await FileSystem.unlink(Entry.EntryPath);\n }\n }\n catch (ErrorValue) {\n if (!IsMissingFileError(ErrorValue)) {\n throw ErrorValue;\n }\n }\n Progress.DeletedEntries += 1;\n Progress.DeletedBytes += Entry.Size;\n Options.OnProgress?.(CloneProgress(Progress));\n }\n Progress.Phase = \"Done\";\n Progress.CurrentPath = null;\n Options.OnProgress?.(CloneProgress(Progress));\n}\nfunction FormatBytes(Bytes: number): string {\n const Units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n let Value = Bytes;\n let UnitIndex = 0;\n while (Value >= 1024 && UnitIndex < Units.length - 1) {\n Value /= 1024;\n UnitIndex += 1;\n }\n return `${Value.toFixed(UnitIndex === 0 ? 0 : 1)} ${Units[UnitIndex]}`;\n}\n"],
5
- "mappings": "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",
6
- "names": ["Path"]
3
+ "sources": ["../Source/FileSystem/FileSystem.ts", "../Source/FileSystem/Module/index.ts", "../Source/String/String.ts", "../Source/FileSystem/Module/Module.ts"],
4
+ "sourcesContent": ["/**\n * @file FileSystem.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nimport * as FileSystem from \"node:fs/promises\";\nimport * as Path from \"node:path\";\nimport { basename, dirname, extname, join } from \"path\";\nimport { type FFileExtension } from \"./FileSystem.Types.ts\";\nimport { type FileHandle } from \"fs/promises\";\nimport { promises as Fs } from \"fs\";\nimport { constants as FsConstants } from \"fs\";\nimport os from \"os\";\nasync function PathExists(Path: string): Promise<boolean> {\n try {\n await Fs.access(Path, FsConstants.F_OK);\n return true;\n }\n catch {\n return false;\n }\n}\n/**\n * @param Extension - The file extension that you wish to test.\n * @returns Whether the file extension is valid on the current platform.\n */\nexport function IsSupportedFileExtension(Extension: FFileExtension): boolean {\n let NormalizedExtension: string = Extension.slice(1);\n if (NormalizedExtension.startsWith(\".\")) {\n NormalizedExtension = NormalizedExtension.slice(1);\n }\n if (NormalizedExtension.length === 0) {\n return false;\n }\n if (NormalizedExtension.includes(\"/\")\n || NormalizedExtension.includes(\"\\\\\")\n || NormalizedExtension.includes(\"\\0\")) {\n return false;\n }\n if (os.platform() === \"win32\") {\n /* eslint-disable-next-line no-control-regex */\n const HasIllegalCharacters: boolean = /[<>:\"/\\\\|?*\\x00-\\x1F]/u.test(NormalizedExtension);\n if (HasIllegalCharacters) {\n return false;\n }\n if (/[ .]$/u.test(NormalizedExtension)) {\n return false;\n }\n }\n return true;\n}\n/**\n * Given a path at which we wish to create a new file, check if a file at that\n * path already exists, and if so, then append `(${ number })` before the file\n * extension, consistent with the Windows Explorer handles conflicting file\n * names when pasting files.\n */\nexport async function GetSafeNewPath(InPath: string): Promise<string> {\n const DirectoryPath: string = dirname(InPath);\n const Extension: string = extname(InPath);\n const FileName: string = basename(InPath);\n const BaseFileName: string = Extension === \"\"\n ? FileName\n : basename(InPath, Extension);\n let CandidatePath: string = InPath;\n let Index: number = 1;\n while (await PathExists(CandidatePath)) {\n const CandidateFileName: string = `${BaseFileName} (${Index})${Extension}`;\n CandidatePath = join(DirectoryPath, CandidateFileName);\n Index++;\n }\n return CandidatePath;\n}\n/* eslint-disable-next-line @typescript-eslint/typedef */\nexport const ReservedWindowsFileNames = [\n \"CON\",\n \"PRN\",\n \"AUX\",\n \"NUL\",\n \"COM1\",\n \"COM2\",\n \"COM3\",\n \"COM4\",\n \"COM5\",\n \"COM6\",\n \"COM7\",\n \"COM8\",\n \"COM9\",\n \"LPT1\",\n \"LPT2\",\n \"LPT3\",\n \"LPT4\",\n \"LPT5\",\n \"LPT6\",\n \"LPT7\",\n \"LPT8\",\n \"LPT9\",\n] as const;\nexport const InvalidCharacters: RegExp = /[<>:\"/\\\\|?*\\u0000-\\u001F]/u;\n/**\n * @param DirectoryPath - The path to the directory in which you wish to check.\n * @param FileName - The desired file name.\n * @param PersistNewFile - *(Optional)* Whether to keep the otherwise-temporary\n * file created at the desired path.\n * @param Extension - *(Optional)* If provided, the function will only return `true`\n * if `FileName.endsWith(Extension)` *and* the `Extension` is a valid\n * file extension.\n * @returns Whether a file of the given `FileName` can be created in `DirectoryPath`.\n *\n * @remarks This *does* attempt to create a file at the desired path. The file is\n * temporary iff `!PersistNewFile`, and is never created when this function\n * returns `false`.\n */\nexport async function IsValidFileName(DirectoryPath: string, FileName: string, PersistNewFile: boolean = false, Extension: FFileExtension | undefined = undefined): Promise<boolean> {\n const ExtensionSafe: FFileExtension | null | \"\" = Extension !== undefined\n ? (Extension.startsWith(\".\") && IsSupportedFileExtension(Extension))\n ? Extension\n : null\n : \"\";\n const IsExtensionImproper: boolean = (ExtensionSafe === null ||\n ExtensionSafe === \".\" ||\n !FileName.endsWith(ExtensionSafe));\n if (IsExtensionImproper) {\n return false;\n }\n const FilePath: string = join(DirectoryPath, FileName);\n try {\n const ThisFileHandle: FileHandle = await Fs.open(FilePath, \"wx\");\n await ThisFileHandle.close();\n if (!PersistNewFile) {\n await Fs.unlink(FilePath);\n }\n return true;\n }\n catch {\n return false;\n }\n}\n/**\n * Write a text file to a given {@link Path} having contents {@link Contents}.\n *\n * @param Path - The path of the file that will be written.\n * @param Contents - The text contents of the file to write.\n *\n * @returns {Promise<void>} A {@link Promise} that resolves when the call to {@link Fs.writeFile} resolves.\n *\n * @example\n * ```typescript\n * import { WriteTextFile } from \"@sorrell/utilities/fs\";\n * import { resolve } from \"path\";\n *\n * const MyReadMe: string = \"# ReadMe\\n\\nThis package accomplishes...\\n\";\n * const MyReadMePath: string = resolve(\".\");\n *\n * await WriteTextFile(MyReadMePath, MyReadMe);\n * ```\n */\nexport async function WriteTextFile(Path: string, Contents: string): Promise<void> {\n await Fs.writeFile(Path, Contents, { encoding: \"utf-8\" });\n}\ntype DeletePhase = \"Scanning\" | \"Deleting\" | \"Done\";\ntype DeleteEntryKind = \"File\" | \"Directory\" | \"Other\";\ntype DeleteEntry = {\n EntryPath: string;\n Kind: DeleteEntryKind;\n Size: number;\n};\ntype DeleteProgress = {\n Phase: DeletePhase;\n CurrentPath: string | null;\n DiscoveredEntries: number;\n TotalEntries: number;\n DeletedEntries: number;\n TotalBytes: number;\n DeletedBytes: number;\n};\ntype DeleteWithProgressOptions = {\n OnProgress?: (Progress: DeleteProgress) => void;\n Signal?: AbortSignal;\n};\nfunction IsMissingFileError(ErrorValue: unknown): boolean {\n return (typeof ErrorValue === \"object\" &&\n ErrorValue !== null &&\n \"code\" in ErrorValue &&\n ErrorValue.code === \"ENOENT\");\n}\nfunction CloneProgress(Progress: DeleteProgress): DeleteProgress {\n return { ...Progress };\n}\nasync function BuildDeletionPlan(RootPath: string, Progress: DeleteProgress, Options: DeleteWithProgressOptions): Promise<DeleteEntry[]> {\n const Entries: DeleteEntry[] = [];\n async function Visit(CurrentPath: string): Promise<void> {\n Options.Signal?.throwIfAborted();\n Progress.Phase = \"Scanning\";\n Progress.CurrentPath = CurrentPath;\n Options.OnProgress?.(CloneProgress(Progress));\n let Stats;\n try {\n Stats = await FileSystem.lstat(CurrentPath);\n }\n catch (ErrorValue) {\n if (IsMissingFileError(ErrorValue)) {\n return;\n }\n throw ErrorValue;\n }\n if (Stats.isDirectory()) {\n const Children = await FileSystem.readdir(CurrentPath, {\n withFileTypes: true\n });\n for (const Child of Children) {\n await Visit(Path.join(CurrentPath, Child.name));\n }\n Entries.push({\n EntryPath: CurrentPath,\n Kind: \"Directory\",\n Size: 0\n });\n }\n else {\n const Size = Stats.isFile() ? Stats.size : 0;\n Entries.push({\n EntryPath: CurrentPath,\n Kind: Stats.isFile() ? \"File\" : \"Other\",\n Size\n });\n Progress.TotalBytes += Size;\n }\n Progress.DiscoveredEntries = Entries.length;\n Options.OnProgress?.(CloneProgress(Progress));\n }\n await Visit(RootPath);\n return Entries;\n}\nexport async function DeleteWithProgress(RootPath: string, Options: DeleteWithProgressOptions = {}): Promise<void> {\n const Progress: DeleteProgress = {\n Phase: \"Scanning\",\n CurrentPath: null,\n DiscoveredEntries: 0,\n TotalEntries: 0,\n DeletedEntries: 0,\n TotalBytes: 0,\n DeletedBytes: 0\n };\n const Entries = await BuildDeletionPlan(RootPath, Progress, Options);\n Progress.Phase = \"Deleting\";\n Progress.TotalEntries = Entries.length;\n Progress.CurrentPath = null;\n Options.OnProgress?.(CloneProgress(Progress));\n for (const Entry of Entries) {\n Options.Signal?.throwIfAborted();\n Progress.CurrentPath = Entry.EntryPath;\n Options.OnProgress?.(CloneProgress(Progress));\n try {\n if (Entry.Kind === \"Directory\") {\n await FileSystem.rmdir(Entry.EntryPath);\n }\n else {\n await FileSystem.unlink(Entry.EntryPath);\n }\n }\n catch (ErrorValue) {\n if (!IsMissingFileError(ErrorValue)) {\n throw ErrorValue;\n }\n }\n Progress.DeletedEntries += 1;\n Progress.DeletedBytes += Entry.Size;\n Options.OnProgress?.(CloneProgress(Progress));\n }\n Progress.Phase = \"Done\";\n Progress.CurrentPath = null;\n Options.OnProgress?.(CloneProgress(Progress));\n}\nfunction FormatBytes(Bytes: number): string {\n const Units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n let Value = Bytes;\n let UnitIndex = 0;\n while (Value >= 1024 && UnitIndex < Units.length - 1) {\n Value /= 1024;\n UnitIndex += 1;\n }\n return `${Value.toFixed(UnitIndex === 0 ? 0 : 1)} ${Units[UnitIndex]}`;\n}\n", "/**\n * @file index.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nexport * from \"./Module.ts\";\nexport * from \"./Module.Types.ts\";\n", "/**\n * @file String.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\n/**\n * @module String\n * Functions for manipulating strings.\n */\n// @TODO TEMPORARY.\n/* eslint-disable jsdoc/require-example */\n/**\n * Does the given {@link TestString} contain only letters?\n * This is equivalent to Python's\n * {@link https://docs.python.org/3/library/stdtypes.html#str.isalpha | isalpha} function.\n *\n * @remarks This is an alias for {@link IsAlpha}.\n *\n * @param TestString - The string that you wish to test.\n *\n * @returns {boolean} Whether the given string contains *only* (Latin alphabet) letters.\n */\nexport function IsLetters(TestString: string): boolean {\n return /^[a-zA-Z]*$/.test(TestString);\n}\n/**\n * Does the given {@link TestString} contain only letters?\n * This is equivalent to Python's\n * {@link https://docs.python.org/3/library/stdtypes.html#str.isalpha | isalpha} function.\n *\n * @param TestString - The string that you wish to test.\n *\n * @returns {boolean} Whether the given string contains *only* (Latin alphabet) letters.\n */\nexport function IsAlpha(TestString: string): boolean {\n return IsLetters(TestString);\n}\n/**\n * Does the given {@link TestString} contain only digits?\n *\n * This is equivalent to Python's\n * {@link https://docs.python.org/3/library/stdtypes.html#str.isnumeric | isnumeric} function.\n *\n * @param TestString - The string that you wish to test.\n *\n * @returns {boolean} Whether the given string contains *only* numeric digits.\n */\nexport function IsNumeric(TestString: string): boolean {\n return /^\\d+$/.test(TestString);\n}\n/**\n * This function provides a convenient way to define long strings across multiple lines.\n * The linebreaks and leading indentation are removed from the final string.\n * Please see the example below for how this is to be done.\n *\n * @param Content - The given string to dedent.\n * @param IndentLength - The number of spaces for which this function will check. This\n * is necessary only if the number of spaces used in the {@link Content} is not a multiple\n * of two.\n *\n * @returns {string} The given {@link Content}, but with the spaces at the beginning\n * of each line (excluding the first line if no spaces exist in the first line) removed.\n *\n * @example\n * ```typescript\n * import { Dedent } from \"@sorrell/utilities/string\";\n *\n * const MyLongString: string = Dedent(`Lorem ipsum dolor sit amet, consectetur adipiscing elit,\n * sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad\n * minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea\n * commodo consequat.`);\n *\n * // `MyLongString` <- `\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, \\\n * sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad \\\n * minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea \\\n * commodo consequat.\"`\n * ```\n */\nexport function Dedent(Content: string, IndentLength?: number): string {\n const WhitespaceSubstring: string = ((): string => {\n if (IndentLength !== undefined) {\n return \"\\n\" + \" \".repeat(IndentLength);\n }\n let WhitespaceSubstring: string = \"\\n\";\n while (true) {\n const TestString: string = WhitespaceSubstring + \" \";\n if (Content.includes(TestString)) {\n WhitespaceSubstring = TestString;\n }\n else {\n break;\n }\n }\n return WhitespaceSubstring;\n })();\n return Content.replaceAll(WhitespaceSubstring, \"\");\n}\nexport function GetUtf8ByteLength(Text: string): number {\n return new TextEncoder().encode(Text).length;\n}\n", "/**\n * @file Module.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nimport { InvalidCharacters, ReservedWindowsFileNames } from \"../FileSystem.ts\";\nimport type { ExtensionPolicy } from \"./Module.Types.ts\";\nimport { GetUtf8ByteLength } from \"../../String/String.ts\";\n/* eslint-disable @typescript-eslint/typedef */\nexport namespace ValidExtensions {\n export /**\n * The valid file extensions for TypeScript *source* modules.\n */ const Source = [\n \".ts\",\n \".tsx\",\n \".mts\",\n \".cts\"\n ] as const;\n export /**\n * The valid file extensions for TypeScript *declaration* modules.\n */ const Declaration = [\n \".d.ts\",\n \".d.mts\",\n \".d.cts\"\n ] as const;\n export /**\n * The valid file extensions for *any* TypeScript module.\n */ const Any = [\n \".ts\",\n \".tsx\",\n \".mts\",\n \".cts\",\n \".d.ts\",\n \".d.mts\",\n \".d.cts\"\n ] as const;\n}\n/* eslint-enable @typescript-eslint/typedef */\nexport function HasTypeScriptExtension(FileName: string): boolean {\n return /\\.(ts|tsx|mts|cts|d\\.ts|d\\.mts|d\\.cts)$/iu.test(FileName);\n}\nexport function IsValidFileName(FileName: string, ExtensionPolicy: ExtensionPolicy = \"Disallow\"): boolean {\n if (FileName.length === 0) {\n return false;\n }\n if (FileName === \".\" || FileName === \"..\") {\n return false;\n }\n const DoesExtensionSatisfyPolicy: boolean = ((ExtensionPolicy === \"Disallow\" && !HasTypeScriptExtension(FileName)) ||\n (ExtensionPolicy === \"Require\" && HasTypeScriptExtension(FileName)) ||\n (Array.isArray(ExtensionPolicy) && ExtensionPolicy.some(FileName.endsWith)));\n if (!DoesExtensionSatisfyPolicy) {\n return false;\n }\n if (FileName.endsWith(\".\") || FileName.endsWith(\" \")) {\n return false;\n }\n if (GetUtf8ByteLength(FileName) > 255) {\n return false;\n }\n if (InvalidCharacters.test(FileName)) {\n return false;\n }\n const FileNameWithoutDots: string | undefined = FileName.split(\".\")[0]?.toUpperCase();\n const FileNameContainsWindowsReserved: boolean = (FileNameWithoutDots !== undefined &&\n (ReservedWindowsFileNames as ReadonlyArray<string>).includes(FileNameWithoutDots));\n if (FileNameContainsWindowsReserved) {\n return false;\n }\n return true;\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["join", "Path", "IsValidFileName", "ValidExtensions", "IsValidFileName"]
7
7
  }
@@ -265,18 +265,21 @@ var FileSystem_exports = {};
265
265
  __export(FileSystem_exports, {
266
266
  DeleteWithProgress: () => DeleteWithProgress,
267
267
  GetSafeNewPath: () => GetSafeNewPath,
268
+ InvalidCharacters: () => InvalidCharacters,
268
269
  IsSupportedFileExtension: () => IsSupportedFileExtension,
269
270
  IsValidFileName: () => IsValidFileName,
271
+ Module: () => Module_exports,
272
+ ReservedWindowsFileNames: () => ReservedWindowsFileNames,
270
273
  WriteTextFile: () => WriteTextFile
271
274
  });
272
275
 
273
276
  // Source/FileSystem/FileSystem.ts
277
+ var FileSystem = __toESM(require("node:fs/promises"), 1);
278
+ var Path = __toESM(require("node:path"), 1);
274
279
  var import_path = require("path");
275
280
  var import_fs = require("fs");
276
281
  var import_fs2 = require("fs");
277
282
  var import_os = __toESM(require("os"), 1);
278
- var FileSystem = __toESM(require("node:fs/promises"), 1);
279
- var Path = __toESM(require("node:path"), 1);
280
283
  async function PathExists(Path2) {
281
284
  try {
282
285
  await import_fs.promises.access(Path2, import_fs2.constants.F_OK);
@@ -321,6 +324,31 @@ async function GetSafeNewPath(InPath) {
321
324
  }
322
325
  return CandidatePath;
323
326
  }
327
+ var ReservedWindowsFileNames = [
328
+ "CON",
329
+ "PRN",
330
+ "AUX",
331
+ "NUL",
332
+ "COM1",
333
+ "COM2",
334
+ "COM3",
335
+ "COM4",
336
+ "COM5",
337
+ "COM6",
338
+ "COM7",
339
+ "COM8",
340
+ "COM9",
341
+ "LPT1",
342
+ "LPT2",
343
+ "LPT3",
344
+ "LPT4",
345
+ "LPT5",
346
+ "LPT6",
347
+ "LPT7",
348
+ "LPT8",
349
+ "LPT9"
350
+ ];
351
+ var InvalidCharacters = /[<>:"/\\|?*\u0000-\u001F]/u;
324
352
  async function IsValidFileName(DirectoryPath, FileName, PersistNewFile = false, Extension = void 0) {
325
353
  const ExtensionSafe = Extension !== void 0 ? Extension.startsWith(".") && IsSupportedFileExtension(Extension) ? Extension : null : "";
326
354
  const IsExtensionImproper = ExtensionSafe === null || ExtensionSafe === "." || !FileName.endsWith(ExtensionSafe);
@@ -430,6 +458,101 @@ async function DeleteWithProgress(RootPath, Options = {}) {
430
458
  Options.OnProgress?.(CloneProgress(Progress));
431
459
  }
432
460
 
461
+ // Source/FileSystem/Module/index.ts
462
+ var Module_exports = {};
463
+ __export(Module_exports, {
464
+ HasTypeScriptExtension: () => HasTypeScriptExtension,
465
+ IsValidFileName: () => IsValidFileName2,
466
+ ValidExtensions: () => ValidExtensions
467
+ });
468
+
469
+ // Source/String/String.ts
470
+ function IsLetters(TestString) {
471
+ return /^[a-zA-Z]*$/.test(TestString);
472
+ }
473
+ function IsAlpha(TestString) {
474
+ return IsLetters(TestString);
475
+ }
476
+ function IsNumeric(TestString) {
477
+ return /^\d+$/.test(TestString);
478
+ }
479
+ function Dedent(Content, IndentLength) {
480
+ const WhitespaceSubstring = (() => {
481
+ if (IndentLength !== void 0) {
482
+ return "\n" + " ".repeat(IndentLength);
483
+ }
484
+ let WhitespaceSubstring2 = "\n";
485
+ while (true) {
486
+ const TestString = WhitespaceSubstring2 + " ";
487
+ if (Content.includes(TestString)) {
488
+ WhitespaceSubstring2 = TestString;
489
+ } else {
490
+ break;
491
+ }
492
+ }
493
+ return WhitespaceSubstring2;
494
+ })();
495
+ return Content.replaceAll(WhitespaceSubstring, "");
496
+ }
497
+ function GetUtf8ByteLength(Text) {
498
+ return new TextEncoder().encode(Text).length;
499
+ }
500
+
501
+ // Source/FileSystem/Module/Module.ts
502
+ var ValidExtensions;
503
+ ((ValidExtensions2) => {
504
+ ValidExtensions2.Source = [
505
+ ".ts",
506
+ ".tsx",
507
+ ".mts",
508
+ ".cts"
509
+ ];
510
+ ValidExtensions2.Declaration = [
511
+ ".d.ts",
512
+ ".d.mts",
513
+ ".d.cts"
514
+ ];
515
+ ValidExtensions2.Any = [
516
+ ".ts",
517
+ ".tsx",
518
+ ".mts",
519
+ ".cts",
520
+ ".d.ts",
521
+ ".d.mts",
522
+ ".d.cts"
523
+ ];
524
+ })(ValidExtensions || (ValidExtensions = {}));
525
+ function HasTypeScriptExtension(FileName) {
526
+ return /\.(ts|tsx|mts|cts|d\.ts|d\.mts|d\.cts)$/iu.test(FileName);
527
+ }
528
+ function IsValidFileName2(FileName, ExtensionPolicy = "Disallow") {
529
+ if (FileName.length === 0) {
530
+ return false;
531
+ }
532
+ if (FileName === "." || FileName === "..") {
533
+ return false;
534
+ }
535
+ const DoesExtensionSatisfyPolicy = ExtensionPolicy === "Disallow" && !HasTypeScriptExtension(FileName) || ExtensionPolicy === "Require" && HasTypeScriptExtension(FileName) || Array.isArray(ExtensionPolicy) && ExtensionPolicy.some(FileName.endsWith);
536
+ if (!DoesExtensionSatisfyPolicy) {
537
+ return false;
538
+ }
539
+ if (FileName.endsWith(".") || FileName.endsWith(" ")) {
540
+ return false;
541
+ }
542
+ if (GetUtf8ByteLength(FileName) > 255) {
543
+ return false;
544
+ }
545
+ if (InvalidCharacters.test(FileName)) {
546
+ return false;
547
+ }
548
+ const FileNameWithoutDots = FileName.split(".")[0]?.toUpperCase();
549
+ const FileNameContainsWindowsReserved = FileNameWithoutDots !== void 0 && ReservedWindowsFileNames.includes(FileNameWithoutDots);
550
+ if (FileNameContainsWindowsReserved) {
551
+ return false;
552
+ }
553
+ return true;
554
+ }
555
+
433
556
  // Source/Functional/index.ts
434
557
  var Functional_exports = {};
435
558
  __export(Functional_exports, {
@@ -743,40 +866,12 @@ function FlatMap(InRecord, InFunction) {
743
866
  var String_exports = {};
744
867
  __export(String_exports, {
745
868
  Dedent: () => Dedent,
869
+ GetUtf8ByteLength: () => GetUtf8ByteLength,
746
870
  IsAlpha: () => IsAlpha,
747
871
  IsLetters: () => IsLetters,
748
872
  IsNumeric: () => IsNumeric
749
873
  });
750
874
 
751
- // Source/String/String.ts
752
- function IsLetters(TestString) {
753
- return /^[a-zA-Z]*$/.test(TestString);
754
- }
755
- function IsAlpha(TestString) {
756
- return IsLetters(TestString);
757
- }
758
- function IsNumeric(TestString) {
759
- return /^\d+$/.test(TestString);
760
- }
761
- function Dedent(Content, IndentLength) {
762
- const WhitespaceSubstring = (() => {
763
- if (IndentLength !== void 0) {
764
- return "\n" + " ".repeat(IndentLength);
765
- }
766
- let WhitespaceSubstring2 = "\n";
767
- while (true) {
768
- const TestString = WhitespaceSubstring2 + " ";
769
- if (Content.includes(TestString)) {
770
- WhitespaceSubstring2 = TestString;
771
- } else {
772
- break;
773
- }
774
- }
775
- return WhitespaceSubstring2;
776
- })();
777
- return Content.replaceAll(WhitespaceSubstring, "");
778
- }
779
-
780
875
  // Source/TsConfig/index.ts
781
876
  var TsConfig_exports = {};
782
877
 
@@ -845,6 +940,24 @@ var Type_exports = {};
845
940
  * @copyright (c) 2026 Gage Sorrell
846
941
  * @license MIT
847
942
  */
943
+ /**
944
+ * @file String.ts
945
+ * @author Gage Sorrell <gage@sorrell.sh>
946
+ * @copyright (c) 2026 Gage Sorrell
947
+ * @license MIT
948
+ */
949
+ /**
950
+ * @file Module.ts
951
+ * @author Gage Sorrell <gage@sorrell.sh>
952
+ * @copyright (c) 2026 Gage Sorrell
953
+ * @license MIT
954
+ */
955
+ /**
956
+ * @file Module.Types.ts
957
+ * @author Gage Sorrell <gage@sorrell.sh>
958
+ * @copyright (c) 2026 Gage Sorrell
959
+ * @license MIT
960
+ */
848
961
  /**
849
962
  * @file Functional.ts
850
963
  * @author Gage Sorrell <gage@sorrell.sh>
@@ -917,12 +1030,6 @@ var Type_exports = {};
917
1030
  * @copyright (c) 2026 Gage Sorrell
918
1031
  * @license MIT
919
1032
  */
920
- /**
921
- * @file String.ts
922
- * @author Gage Sorrell <gage@sorrell.sh>
923
- * @copyright (c) 2026 Gage Sorrell
924
- * @license MIT
925
- */
926
1033
  /**
927
1034
  * @file TsConfig.Types.ts
928
1035
  * @author Gage Sorrell <gage@sorrell.sh>