@sorrell/utilities 1.2.8 → 1.2.11

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 (96) hide show
  1. package/Distribution/PackageExports.Generated.json +30 -0
  2. package/Distribution/Types/FileSystem/FileSystem.d.cts +16 -0
  3. package/Distribution/Types/FileSystem/FileSystem.d.cts.map +1 -1
  4. package/Distribution/Types/FileSystem/FileSystem.d.mts +16 -0
  5. package/Distribution/Types/FileSystem/FileSystem.d.mts.map +1 -1
  6. package/Distribution/Types/FileSystem/FileSystem.d.ts +16 -0
  7. package/Distribution/Types/FileSystem/FileSystem.d.ts.map +1 -1
  8. package/Distribution/Types/Functional/Functional.d.cts +7 -1
  9. package/Distribution/Types/Functional/Functional.d.cts.map +1 -1
  10. package/Distribution/Types/Functional/Functional.d.mts +7 -1
  11. package/Distribution/Types/Functional/Functional.d.mts.map +1 -1
  12. package/Distribution/Types/Functional/Functional.d.ts +7 -1
  13. package/Distribution/Types/Functional/Functional.d.ts.map +1 -1
  14. package/Distribution/Types/HigherKind/HigherKind.Internal.Types.d.cts +30 -0
  15. package/Distribution/Types/HigherKind/HigherKind.Internal.Types.d.cts.map +10 -0
  16. package/Distribution/Types/HigherKind/HigherKind.Internal.Types.d.mts +30 -0
  17. package/Distribution/Types/HigherKind/HigherKind.Internal.Types.d.mts.map +10 -0
  18. package/Distribution/Types/HigherKind/HigherKind.Internal.Types.d.ts +30 -0
  19. package/Distribution/Types/HigherKind/HigherKind.Internal.Types.d.ts.map +1 -0
  20. package/Distribution/Types/HigherKind/HigherKind.Registrar.Types.d.cts +31 -0
  21. package/Distribution/Types/HigherKind/HigherKind.Registrar.Types.d.cts.map +10 -0
  22. package/Distribution/Types/HigherKind/HigherKind.Registrar.Types.d.mts +31 -0
  23. package/Distribution/Types/HigherKind/HigherKind.Registrar.Types.d.mts.map +10 -0
  24. package/Distribution/Types/HigherKind/HigherKind.Registrar.Types.d.ts +31 -0
  25. package/Distribution/Types/HigherKind/HigherKind.Registrar.Types.d.ts.map +1 -0
  26. package/Distribution/Types/HigherKind/HigherKind.Types.d.cts +85 -0
  27. package/Distribution/Types/HigherKind/HigherKind.Types.d.cts.map +10 -0
  28. package/Distribution/Types/HigherKind/HigherKind.Types.d.mts +85 -0
  29. package/Distribution/Types/HigherKind/HigherKind.Types.d.mts.map +10 -0
  30. package/Distribution/Types/HigherKind/HigherKind.Types.d.ts +85 -0
  31. package/Distribution/Types/HigherKind/HigherKind.Types.d.ts.map +1 -0
  32. package/Distribution/Types/HigherKind/index.d.cts +8 -0
  33. package/Distribution/Types/HigherKind/index.d.cts.map +10 -0
  34. package/Distribution/Types/HigherKind/index.d.mts +8 -0
  35. package/Distribution/Types/HigherKind/index.d.mts.map +10 -0
  36. package/Distribution/Types/HigherKind/index.d.ts +8 -0
  37. package/Distribution/Types/HigherKind/index.d.ts.map +1 -0
  38. package/Distribution/Types/Miscellaneous/Utility.Types.d.cts +12 -26
  39. package/Distribution/Types/Miscellaneous/Utility.Types.d.cts.map +1 -1
  40. package/Distribution/Types/Miscellaneous/Utility.Types.d.mts +12 -26
  41. package/Distribution/Types/Miscellaneous/Utility.Types.d.mts.map +1 -1
  42. package/Distribution/Types/Miscellaneous/Utility.Types.d.ts +12 -26
  43. package/Distribution/Types/Miscellaneous/Utility.Types.d.ts.map +1 -1
  44. package/Distribution/Types/String/String.d.cts +55 -10
  45. package/Distribution/Types/String/String.d.cts.map +1 -1
  46. package/Distribution/Types/String/String.d.mts +55 -10
  47. package/Distribution/Types/String/String.d.mts.map +1 -1
  48. package/Distribution/Types/String/String.d.ts +55 -10
  49. package/Distribution/Types/String/String.d.ts.map +1 -1
  50. package/Distribution/Types/TsConfig/TsConfig.Types.d.cts +32 -0
  51. package/Distribution/Types/TsConfig/TsConfig.Types.d.cts.map +10 -0
  52. package/Distribution/Types/TsConfig/TsConfig.Types.d.mts +32 -0
  53. package/Distribution/Types/TsConfig/TsConfig.Types.d.mts.map +10 -0
  54. package/Distribution/Types/TsConfig/TsConfig.Types.d.ts +32 -0
  55. package/Distribution/Types/TsConfig/TsConfig.Types.d.ts.map +1 -0
  56. package/Distribution/Types/TsConfig/index.d.cts +8 -0
  57. package/Distribution/Types/TsConfig/index.d.cts.map +10 -0
  58. package/Distribution/Types/TsConfig/index.d.mts +8 -0
  59. package/Distribution/Types/TsConfig/index.d.mts.map +10 -0
  60. package/Distribution/Types/TsConfig/index.d.ts +8 -0
  61. package/Distribution/Types/TsConfig/index.d.ts.map +1 -0
  62. package/Distribution/Types/index.d.cts +1 -0
  63. package/Distribution/Types/index.d.cts.map +1 -1
  64. package/Distribution/Types/index.d.mts +1 -0
  65. package/Distribution/Types/index.d.mts.map +1 -1
  66. package/Distribution/Types/index.d.ts +1 -0
  67. package/Distribution/Types/index.d.ts.map +1 -1
  68. package/Distribution/async.cjs.map +2 -2
  69. package/Distribution/async.js.map +2 -2
  70. package/Distribution/fs.cjs +94 -4
  71. package/Distribution/fs.cjs.map +3 -3
  72. package/Distribution/fs.js +94 -4
  73. package/Distribution/fs.js.map +3 -3
  74. package/Distribution/functional.cjs +2 -2
  75. package/Distribution/functional.cjs.map +2 -2
  76. package/Distribution/functional.js +2 -2
  77. package/Distribution/functional.js.map +2 -2
  78. package/Distribution/higher-kind.cjs +31 -0
  79. package/Distribution/higher-kind.cjs.map +7 -0
  80. package/Distribution/higher-kind.js +13 -0
  81. package/Distribution/higher-kind.js.map +7 -0
  82. package/Distribution/index.cjs +137 -18
  83. package/Distribution/index.cjs.map +4 -4
  84. package/Distribution/index.js +144 -25
  85. package/Distribution/index.js.map +4 -4
  86. package/Distribution/misc.cjs.map +2 -2
  87. package/Distribution/misc.js.map +2 -2
  88. package/Distribution/string.cjs +70 -0
  89. package/Distribution/string.cjs.map +7 -0
  90. package/Distribution/string.js +47 -0
  91. package/Distribution/string.js.map +7 -0
  92. package/Distribution/tsconfig.cjs +31 -0
  93. package/Distribution/tsconfig.cjs.map +7 -0
  94. package/Distribution/tsconfig.js +13 -0
  95. package/Distribution/tsconfig.js.map +7 -0
  96. package/package.json +45 -7
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../Source/FileSystem/index.ts", "../Source/FileSystem/FileSystem.ts"],
4
- "sourcesContent": ["/**\n * @file index.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nexport * from \"./FileSystem.ts\";\nexport * from \"./FileSystem.Types.ts\";\n", "/**\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}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACMA,kBAAiD;AAGjD,gBAA+B;AAC/B,IAAAA,aAAyC;AACzC,gBAAe;AACf,eAAe,WAAW,MAAgC;AACtD,MAAI;AACA,UAAM,UAAAC,SAAG,OAAO,MAAM,WAAAC,UAAY,IAAI;AACtC,WAAO;AAAA,EACX,QACM;AACF,WAAO;AAAA,EACX;AACJ;AAKO,SAAS,yBAAyB,WAAoC;AACzE,MAAI,sBAA8B,UAAU,MAAM,CAAC;AACnD,MAAI,oBAAoB,WAAW,GAAG,GAAG;AACrC,0BAAsB,oBAAoB,MAAM,CAAC;AAAA,EACrD;AACA,MAAI,oBAAoB,WAAW,GAAG;AAClC,WAAO;AAAA,EACX;AACA,MAAI,oBAAoB,SAAS,GAAG,KAC7B,oBAAoB,SAAS,IAAI,KACjC,oBAAoB,SAAS,IAAI,GAAG;AACvC,WAAO;AAAA,EACX;AACA,MAAI,UAAAC,QAAG,SAAS,MAAM,SAAS;AAE3B,UAAM,uBAAgC,yBAAyB,KAAK,mBAAmB;AACvF,QAAI,sBAAsB;AACtB,aAAO;AAAA,IACX;AACA,QAAI,SAAS,KAAK,mBAAmB,GAAG;AACpC,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAOA,eAAsB,eAAe,QAAiC;AAClE,QAAM,oBAAwB,qBAAQ,MAAM;AAC5C,QAAM,gBAAoB,qBAAQ,MAAM;AACxC,QAAM,eAAmB,sBAAS,MAAM;AACxC,QAAM,eAAuB,cAAc,KACrC,eACA,sBAAS,QAAQ,SAAS;AAChC,MAAI,gBAAwB;AAC5B,MAAI,QAAgB;AACpB,SAAO,MAAM,WAAW,aAAa,GAAG;AACpC,UAAM,oBAA4B,GAAG,YAAY,KAAK,KAAK,IAAI,SAAS;AACxE,wBAAgB,kBAAK,eAAe,iBAAiB;AACrD;AAAA,EACJ;AACA,SAAO;AACX;AAeA,eAAsB,gBAAgB,eAAuB,UAAkB,iBAA0B,OAAO,YAAwC,QAA6B;AACjL,QAAM,gBAA4C,cAAc,SACzD,UAAU,WAAW,GAAG,KAAK,yBAAyB,SAAS,IAC5D,YACA,OACJ;AACN,QAAM,sBAAgC,kBAAkB,QACpD,kBAAkB,OAClB,CAAC,SAAS,SAAS,aAAa;AACpC,MAAI,qBAAqB;AACrB,WAAO;AAAA,EACX;AACA,QAAM,eAAmB,kBAAK,eAAe,QAAQ;AACrD,MAAI;AACA,UAAM,iBAA6B,MAAM,UAAAF,SAAG,KAAK,UAAU,IAAI;AAC/D,UAAM,eAAe,MAAM;AAC3B,QAAI,CAAC,gBAAgB;AACjB,YAAM,UAAAA,SAAG,OAAO,QAAQ;AAAA,IAC5B;AACA,WAAO;AAAA,EACX,QACM;AACF,WAAO;AAAA,EACX;AACJ;AAoBA,eAAsB,cAAc,MAAc,UAAiC;AAC/E,QAAM,UAAAA,SAAG,UAAU,MAAM,UAAU,EAAE,UAAU,QAAQ,CAAC;AAC5D;",
6
- "names": ["import_fs", "Fs", "FsConstants", "os"]
4
+ "sourcesContent": ["/**\n * @file index.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nexport * from \"./FileSystem.ts\";\nexport * from \"./FileSystem.Types.ts\";\n", "/**\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": ["import_fs", "Path", "Fs", "FsConstants", "os"]
7
7
  }
@@ -3,9 +3,11 @@ import { basename, dirname, extname, join } from "path";
3
3
  import { promises as Fs } from "fs";
4
4
  import { constants as FsConstants } from "fs";
5
5
  import os from "os";
6
- async function PathExists(Path) {
6
+ import * as FileSystem from "node:fs/promises";
7
+ import * as Path from "node:path";
8
+ async function PathExists(Path2) {
7
9
  try {
8
- await Fs.access(Path, FsConstants.F_OK);
10
+ await Fs.access(Path2, FsConstants.F_OK);
9
11
  return true;
10
12
  } catch {
11
13
  return false;
@@ -65,10 +67,98 @@ async function IsValidFileName(DirectoryPath, FileName, PersistNewFile = false,
65
67
  return false;
66
68
  }
67
69
  }
68
- async function WriteTextFile(Path, Contents) {
69
- await Fs.writeFile(Path, Contents, { encoding: "utf-8" });
70
+ async function WriteTextFile(Path2, Contents) {
71
+ await Fs.writeFile(Path2, Contents, { encoding: "utf-8" });
72
+ }
73
+ function IsMissingFileError(ErrorValue) {
74
+ return typeof ErrorValue === "object" && ErrorValue !== null && "code" in ErrorValue && ErrorValue.code === "ENOENT";
75
+ }
76
+ function CloneProgress(Progress) {
77
+ return { ...Progress };
78
+ }
79
+ async function BuildDeletionPlan(RootPath, Progress, Options) {
80
+ const Entries = [];
81
+ async function Visit(CurrentPath) {
82
+ Options.Signal?.throwIfAborted();
83
+ Progress.Phase = "Scanning";
84
+ Progress.CurrentPath = CurrentPath;
85
+ Options.OnProgress?.(CloneProgress(Progress));
86
+ let Stats;
87
+ try {
88
+ Stats = await FileSystem.lstat(CurrentPath);
89
+ } catch (ErrorValue) {
90
+ if (IsMissingFileError(ErrorValue)) {
91
+ return;
92
+ }
93
+ throw ErrorValue;
94
+ }
95
+ if (Stats.isDirectory()) {
96
+ const Children = await FileSystem.readdir(CurrentPath, {
97
+ withFileTypes: true
98
+ });
99
+ for (const Child of Children) {
100
+ await Visit(Path.join(CurrentPath, Child.name));
101
+ }
102
+ Entries.push({
103
+ EntryPath: CurrentPath,
104
+ Kind: "Directory",
105
+ Size: 0
106
+ });
107
+ } else {
108
+ const Size = Stats.isFile() ? Stats.size : 0;
109
+ Entries.push({
110
+ EntryPath: CurrentPath,
111
+ Kind: Stats.isFile() ? "File" : "Other",
112
+ Size
113
+ });
114
+ Progress.TotalBytes += Size;
115
+ }
116
+ Progress.DiscoveredEntries = Entries.length;
117
+ Options.OnProgress?.(CloneProgress(Progress));
118
+ }
119
+ await Visit(RootPath);
120
+ return Entries;
121
+ }
122
+ async function DeleteWithProgress(RootPath, Options = {}) {
123
+ const Progress = {
124
+ Phase: "Scanning",
125
+ CurrentPath: null,
126
+ DiscoveredEntries: 0,
127
+ TotalEntries: 0,
128
+ DeletedEntries: 0,
129
+ TotalBytes: 0,
130
+ DeletedBytes: 0
131
+ };
132
+ const Entries = await BuildDeletionPlan(RootPath, Progress, Options);
133
+ Progress.Phase = "Deleting";
134
+ Progress.TotalEntries = Entries.length;
135
+ Progress.CurrentPath = null;
136
+ Options.OnProgress?.(CloneProgress(Progress));
137
+ for (const Entry of Entries) {
138
+ Options.Signal?.throwIfAborted();
139
+ Progress.CurrentPath = Entry.EntryPath;
140
+ Options.OnProgress?.(CloneProgress(Progress));
141
+ try {
142
+ if (Entry.Kind === "Directory") {
143
+ await FileSystem.rmdir(Entry.EntryPath);
144
+ } else {
145
+ await FileSystem.unlink(Entry.EntryPath);
146
+ }
147
+ } catch (ErrorValue) {
148
+ if (!IsMissingFileError(ErrorValue)) {
149
+ throw ErrorValue;
150
+ }
151
+ }
152
+ Progress.DeletedEntries += 1;
153
+ Progress.DeletedBytes += Entry.Size;
154
+ Options.OnProgress?.(CloneProgress(Progress));
155
+ }
156
+ Progress.Phase = "Done";
157
+ Progress.CurrentPath = null;
158
+ Options.OnProgress?.(CloneProgress(Progress));
70
159
  }
71
160
  export {
161
+ DeleteWithProgress,
72
162
  GetSafeNewPath,
73
163
  IsSupportedFileExtension,
74
164
  IsValidFileName,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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}\n"],
5
- "mappings": ";AAMA,SAAS,UAAU,SAAS,SAAS,YAAY;AAGjD,SAAS,YAAY,UAAU;AAC/B,SAAS,aAAa,mBAAmB;AACzC,OAAO,QAAQ;AACf,eAAe,WAAW,MAAgC;AACtD,MAAI;AACA,UAAM,GAAG,OAAO,MAAM,YAAY,IAAI;AACtC,WAAO;AAAA,EACX,QACM;AACF,WAAO;AAAA,EACX;AACJ;AAKO,SAAS,yBAAyB,WAAoC;AACzE,MAAI,sBAA8B,UAAU,MAAM,CAAC;AACnD,MAAI,oBAAoB,WAAW,GAAG,GAAG;AACrC,0BAAsB,oBAAoB,MAAM,CAAC;AAAA,EACrD;AACA,MAAI,oBAAoB,WAAW,GAAG;AAClC,WAAO;AAAA,EACX;AACA,MAAI,oBAAoB,SAAS,GAAG,KAC7B,oBAAoB,SAAS,IAAI,KACjC,oBAAoB,SAAS,IAAI,GAAG;AACvC,WAAO;AAAA,EACX;AACA,MAAI,GAAG,SAAS,MAAM,SAAS;AAE3B,UAAM,uBAAgC,yBAAyB,KAAK,mBAAmB;AACvF,QAAI,sBAAsB;AACtB,aAAO;AAAA,IACX;AACA,QAAI,SAAS,KAAK,mBAAmB,GAAG;AACpC,aAAO;AAAA,IACX;AAAA,EACJ;AACA,SAAO;AACX;AAOA,eAAsB,eAAe,QAAiC;AAClE,QAAM,gBAAwB,QAAQ,MAAM;AAC5C,QAAM,YAAoB,QAAQ,MAAM;AACxC,QAAM,WAAmB,SAAS,MAAM;AACxC,QAAM,eAAuB,cAAc,KACrC,WACA,SAAS,QAAQ,SAAS;AAChC,MAAI,gBAAwB;AAC5B,MAAI,QAAgB;AACpB,SAAO,MAAM,WAAW,aAAa,GAAG;AACpC,UAAM,oBAA4B,GAAG,YAAY,KAAK,KAAK,IAAI,SAAS;AACxE,oBAAgB,KAAK,eAAe,iBAAiB;AACrD;AAAA,EACJ;AACA,SAAO;AACX;AAeA,eAAsB,gBAAgB,eAAuB,UAAkB,iBAA0B,OAAO,YAAwC,QAA6B;AACjL,QAAM,gBAA4C,cAAc,SACzD,UAAU,WAAW,GAAG,KAAK,yBAAyB,SAAS,IAC5D,YACA,OACJ;AACN,QAAM,sBAAgC,kBAAkB,QACpD,kBAAkB,OAClB,CAAC,SAAS,SAAS,aAAa;AACpC,MAAI,qBAAqB;AACrB,WAAO;AAAA,EACX;AACA,QAAM,WAAmB,KAAK,eAAe,QAAQ;AACrD,MAAI;AACA,UAAM,iBAA6B,MAAM,GAAG,KAAK,UAAU,IAAI;AAC/D,UAAM,eAAe,MAAM;AAC3B,QAAI,CAAC,gBAAgB;AACjB,YAAM,GAAG,OAAO,QAAQ;AAAA,IAC5B;AACA,WAAO;AAAA,EACX,QACM;AACF,WAAO;AAAA,EACX;AACJ;AAoBA,eAAsB,cAAc,MAAc,UAAiC;AAC/E,QAAM,GAAG,UAAU,MAAM,UAAU,EAAE,UAAU,QAAQ,CAAC;AAC5D;",
6
- "names": []
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"]
7
7
  }
@@ -25,8 +25,8 @@ __export(Functional_exports, {
25
25
  module.exports = __toCommonJS(Functional_exports);
26
26
 
27
27
  // Source/Functional/Functional.ts
28
- function Identity(...Arguments) {
29
- return Arguments;
28
+ function Identity(...ArgumentVector) {
29
+ return ArgumentVector;
30
30
  }
31
31
  /**
32
32
  * @file Functional.ts
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../Source/Functional/index.ts", "../Source/Functional/Functional.ts"],
4
- "sourcesContent": ["/**\n * @file index.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nexport * from \"./Functional.ts\";\nexport * from \"./Functional.Types.ts\";\n", "/**\n * @file Functional.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nimport { CurriedArgument } from \"./Functional.Internal\";\nimport type { FCurriedArgument } from \"./Functional.Internal.Types\";\nimport type { TFunction } from \"./Functional.Types\";\nexport function Identity<Type>(...Arguments: Array<Type>) {\n return Arguments;\n}\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACSO,SAAS,YAAkB,WAAwB;AACtD,SAAO;AACX;",
4
+ "sourcesContent": ["/**\n * @file index.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nexport * from \"./Functional.ts\";\nexport * from \"./Functional.Types.ts\";\n", "/**\n * @file Functional.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\n/* eslint-disable jsdoc/require-example */\n/**\n * The identity mapping, for some {@link Array} of {@link ArgumentVector | given arguments}.\n *\n * @param ArgumentVector - The {@link Array} of given arguments.\n * @returns {typeof ArgumentVector} The {@link Array} of given arguments.\n */\nexport function Identity<ArgumentType>(...ArgumentVector: Array<ArgumentType>): typeof ArgumentVector {\n return ArgumentVector;\n}\n/* eslint-enable jsdoc/require-example */\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACaO,SAAS,YAA0B,gBAA4D;AAClG,SAAO;AACX;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,6 @@
1
1
  // Source/Functional/Functional.ts
2
- function Identity(...Arguments) {
3
- return Arguments;
2
+ function Identity(...ArgumentVector) {
3
+ return ArgumentVector;
4
4
  }
5
5
  export {
6
6
  Identity
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../Source/Functional/Functional.ts"],
4
- "sourcesContent": ["/**\n * @file Functional.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nimport { CurriedArgument } from \"./Functional.Internal\";\nimport type { FCurriedArgument } from \"./Functional.Internal.Types\";\nimport type { TFunction } from \"./Functional.Types\";\nexport function Identity<Type>(...Arguments: Array<Type>) {\n return Arguments;\n}\n"],
5
- "mappings": ";AASO,SAAS,YAAkB,WAAwB;AACtD,SAAO;AACX;",
4
+ "sourcesContent": ["/**\n * @file Functional.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\n/* eslint-disable jsdoc/require-example */\n/**\n * The identity mapping, for some {@link Array} of {@link ArgumentVector | given arguments}.\n *\n * @param ArgumentVector - The {@link Array} of given arguments.\n * @returns {typeof ArgumentVector} The {@link Array} of given arguments.\n */\nexport function Identity<ArgumentType>(...ArgumentVector: Array<ArgumentType>): typeof ArgumentVector {\n return ArgumentVector;\n}\n/* eslint-enable jsdoc/require-example */\n"],
5
+ "mappings": ";AAaO,SAAS,YAA0B,gBAA4D;AAClG,SAAO;AACX;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __copyProps = (to, from, except, desc) => {
7
+ if (from && typeof from === "object" || typeof from === "function") {
8
+ for (let key of __getOwnPropNames(from))
9
+ if (!__hasOwnProp.call(to, key) && key !== except)
10
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
11
+ }
12
+ return to;
13
+ };
14
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
15
+
16
+ // Source/HigherKind/index.ts
17
+ var HigherKind_exports = {};
18
+ module.exports = __toCommonJS(HigherKind_exports);
19
+ /**
20
+ * @file HigherKind.Types.ts
21
+ * @author Gage Sorrell <gage@sorrell.sh>
22
+ * @copyright (c) 2026 Gage Sorrell
23
+ * @license MIT
24
+ */
25
+ /**
26
+ * @file index.ts
27
+ * @author Gage Sorrell <gage@sorrell.sh>
28
+ * @copyright (c) 2026 Gage Sorrell
29
+ * @license MIT
30
+ */
31
+ //# sourceMappingURL=higher-kind.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../Source/HigherKind/index.ts"],
4
+ "sourcesContent": ["/**\n * @file index.ts\n * @author Gage Sorrell <gage@sorrell.sh>\n * @copyright (c) 2026 Gage Sorrell\n * @license MIT\n */\nexport * from \"./HigherKind.Types.ts\";\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;AAAA;AAAA;",
6
+ "names": []
7
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @file HigherKind.Types.ts
3
+ * @author Gage Sorrell <gage@sorrell.sh>
4
+ * @copyright (c) 2026 Gage Sorrell
5
+ * @license MIT
6
+ */
7
+ /**
8
+ * @file index.ts
9
+ * @author Gage Sorrell <gage@sorrell.sh>
10
+ * @copyright (c) 2026 Gage Sorrell
11
+ * @license MIT
12
+ */
13
+ //# sourceMappingURL=higher-kind.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -39,7 +39,8 @@ __export(Source_exports, {
39
39
  Miscellaneous: () => Miscellaneous_exports,
40
40
  Npm: () => Npm_exports,
41
41
  Path: () => Path_exports,
42
- String: () => String_exports
42
+ String: () => String_exports,
43
+ TsConfig: () => TsConfig_exports
43
44
  });
44
45
  module.exports = __toCommonJS(Source_exports);
45
46
 
@@ -254,6 +255,7 @@ var i = new FComplex(0, 1);
254
255
  // Source/FileSystem/index.ts
255
256
  var FileSystem_exports = {};
256
257
  __export(FileSystem_exports, {
258
+ DeleteWithProgress: () => DeleteWithProgress,
257
259
  GetSafeNewPath: () => GetSafeNewPath,
258
260
  IsSupportedFileExtension: () => IsSupportedFileExtension,
259
261
  IsValidFileName: () => IsValidFileName,
@@ -265,9 +267,11 @@ var import_path = require("path");
265
267
  var import_fs = require("fs");
266
268
  var import_fs2 = require("fs");
267
269
  var import_os = __toESM(require("os"), 1);
268
- async function PathExists(Path) {
270
+ var FileSystem = __toESM(require("node:fs/promises"), 1);
271
+ var Path = __toESM(require("node:path"), 1);
272
+ async function PathExists(Path2) {
269
273
  try {
270
- await import_fs.promises.access(Path, import_fs2.constants.F_OK);
274
+ await import_fs.promises.access(Path2, import_fs2.constants.F_OK);
271
275
  return true;
272
276
  } catch {
273
277
  return false;
@@ -327,8 +331,95 @@ async function IsValidFileName(DirectoryPath, FileName, PersistNewFile = false,
327
331
  return false;
328
332
  }
329
333
  }
330
- async function WriteTextFile(Path, Contents) {
331
- await import_fs.promises.writeFile(Path, Contents, { encoding: "utf-8" });
334
+ async function WriteTextFile(Path2, Contents) {
335
+ await import_fs.promises.writeFile(Path2, Contents, { encoding: "utf-8" });
336
+ }
337
+ function IsMissingFileError(ErrorValue) {
338
+ return typeof ErrorValue === "object" && ErrorValue !== null && "code" in ErrorValue && ErrorValue.code === "ENOENT";
339
+ }
340
+ function CloneProgress(Progress) {
341
+ return { ...Progress };
342
+ }
343
+ async function BuildDeletionPlan(RootPath, Progress, Options) {
344
+ const Entries = [];
345
+ async function Visit(CurrentPath) {
346
+ Options.Signal?.throwIfAborted();
347
+ Progress.Phase = "Scanning";
348
+ Progress.CurrentPath = CurrentPath;
349
+ Options.OnProgress?.(CloneProgress(Progress));
350
+ let Stats;
351
+ try {
352
+ Stats = await FileSystem.lstat(CurrentPath);
353
+ } catch (ErrorValue) {
354
+ if (IsMissingFileError(ErrorValue)) {
355
+ return;
356
+ }
357
+ throw ErrorValue;
358
+ }
359
+ if (Stats.isDirectory()) {
360
+ const Children = await FileSystem.readdir(CurrentPath, {
361
+ withFileTypes: true
362
+ });
363
+ for (const Child of Children) {
364
+ await Visit(Path.join(CurrentPath, Child.name));
365
+ }
366
+ Entries.push({
367
+ EntryPath: CurrentPath,
368
+ Kind: "Directory",
369
+ Size: 0
370
+ });
371
+ } else {
372
+ const Size = Stats.isFile() ? Stats.size : 0;
373
+ Entries.push({
374
+ EntryPath: CurrentPath,
375
+ Kind: Stats.isFile() ? "File" : "Other",
376
+ Size
377
+ });
378
+ Progress.TotalBytes += Size;
379
+ }
380
+ Progress.DiscoveredEntries = Entries.length;
381
+ Options.OnProgress?.(CloneProgress(Progress));
382
+ }
383
+ await Visit(RootPath);
384
+ return Entries;
385
+ }
386
+ async function DeleteWithProgress(RootPath, Options = {}) {
387
+ const Progress = {
388
+ Phase: "Scanning",
389
+ CurrentPath: null,
390
+ DiscoveredEntries: 0,
391
+ TotalEntries: 0,
392
+ DeletedEntries: 0,
393
+ TotalBytes: 0,
394
+ DeletedBytes: 0
395
+ };
396
+ const Entries = await BuildDeletionPlan(RootPath, Progress, Options);
397
+ Progress.Phase = "Deleting";
398
+ Progress.TotalEntries = Entries.length;
399
+ Progress.CurrentPath = null;
400
+ Options.OnProgress?.(CloneProgress(Progress));
401
+ for (const Entry of Entries) {
402
+ Options.Signal?.throwIfAborted();
403
+ Progress.CurrentPath = Entry.EntryPath;
404
+ Options.OnProgress?.(CloneProgress(Progress));
405
+ try {
406
+ if (Entry.Kind === "Directory") {
407
+ await FileSystem.rmdir(Entry.EntryPath);
408
+ } else {
409
+ await FileSystem.unlink(Entry.EntryPath);
410
+ }
411
+ } catch (ErrorValue) {
412
+ if (!IsMissingFileError(ErrorValue)) {
413
+ throw ErrorValue;
414
+ }
415
+ }
416
+ Progress.DeletedEntries += 1;
417
+ Progress.DeletedBytes += Entry.Size;
418
+ Options.OnProgress?.(CloneProgress(Progress));
419
+ }
420
+ Progress.Phase = "Done";
421
+ Progress.CurrentPath = null;
422
+ Options.OnProgress?.(CloneProgress(Progress));
332
423
  }
333
424
 
334
425
  // Source/Functional/index.ts
@@ -338,8 +429,8 @@ __export(Functional_exports, {
338
429
  });
339
430
 
340
431
  // Source/Functional/Functional.ts
341
- function Identity(...Arguments) {
342
- return Arguments;
432
+ function Identity(...ArgumentVector) {
433
+ return ArgumentVector;
343
434
  }
344
435
 
345
436
  // Source/Math/index.ts
@@ -487,8 +578,8 @@ var RootDirectoryNotFoundError = class extends import_effect.Data.TaggedError("R
487
578
  // Source/Npm/Npm.ts
488
579
  var import_path2 = require("path");
489
580
  var import_process = __toESM(require("process"), 1);
490
- async function GetPackageJson(Path) {
491
- const RootDirectory = await GetPackageRootDirectory(Path);
581
+ async function GetPackageJson(Path2) {
582
+ const RootDirectory = await GetPackageRootDirectory(Path2);
492
583
  const PackageJsonPath = (0, import_path2.join)(RootDirectory, "package.json");
493
584
  const FileContents = await import_fs3.promises.readFile(PackageJsonPath, "utf-8");
494
585
  const PackageJson = await (async () => {
@@ -500,8 +591,8 @@ async function GetPackageJson(Path) {
500
591
  })();
501
592
  return PackageJson;
502
593
  }
503
- async function GetPackageRootDirectory(Path) {
504
- let CurrentDirectory = await import_fs3.promises.realpath(Path ?? import_process.default.cwd());
594
+ async function GetPackageRootDirectory(Path2) {
595
+ let CurrentDirectory = await import_fs3.promises.realpath(Path2 ?? import_process.default.cwd());
505
596
  while (true) {
506
597
  const PackageJsonPath = (0, import_path2.join)(CurrentDirectory, "package.json");
507
598
  const PackageJsonExists = await (async () => {
@@ -517,7 +608,7 @@ async function GetPackageRootDirectory(Path) {
517
608
  }
518
609
  const ParentDirectory = (0, import_path2.dirname)(CurrentDirectory);
519
610
  if (ParentDirectory === CurrentDirectory) {
520
- throw new RootDirectoryNotFoundError({ Path });
611
+ throw new RootDirectoryNotFoundError({ Path: Path2 });
521
612
  }
522
613
  CurrentDirectory = ParentDirectory;
523
614
  }
@@ -557,21 +648,43 @@ var FPath = class _FPath {
557
648
  // Source/String/index.ts
558
649
  var String_exports = {};
559
650
  __export(String_exports, {
651
+ Dedent: () => Dedent,
560
652
  IsAlpha: () => IsAlpha,
561
653
  IsLetters: () => IsLetters,
562
654
  IsNumeric: () => IsNumeric
563
655
  });
564
656
 
565
657
  // Source/String/String.ts
566
- function IsLetters(In) {
567
- return /^[a-zA-Z]*$/.test(In);
658
+ function IsLetters(TestString) {
659
+ return /^[a-zA-Z]*$/.test(TestString);
660
+ }
661
+ function IsAlpha(TestString) {
662
+ return IsLetters(TestString);
568
663
  }
569
- function IsAlpha(In) {
570
- return IsLetters(In);
664
+ function IsNumeric(TestString) {
665
+ return /^\d+$/.test(TestString);
571
666
  }
572
- function IsNumeric(In) {
573
- return /^\d+$/.test(In);
667
+ function Dedent(Content, IndentLength) {
668
+ const WhitespaceSubstring = (() => {
669
+ if (IndentLength !== void 0) {
670
+ return "\n" + " ".repeat(IndentLength);
671
+ }
672
+ let WhitespaceSubstring2 = "\n";
673
+ while (true) {
674
+ const TestString = WhitespaceSubstring2 + " ";
675
+ if (Content.includes(TestString)) {
676
+ WhitespaceSubstring2 = TestString;
677
+ } else {
678
+ break;
679
+ }
680
+ }
681
+ return WhitespaceSubstring2;
682
+ })();
683
+ return Content.replaceAll(WhitespaceSubstring, "");
574
684
  }
685
+
686
+ // Source/TsConfig/index.ts
687
+ var TsConfig_exports = {};
575
688
  /**
576
689
  * @file Array.ts
577
690
  * @author Gage Sorrell <gage@sorrell.sh>
@@ -698,4 +811,10 @@ function IsNumeric(In) {
698
811
  * @copyright (c) 2026 Gage Sorrell
699
812
  * @license MIT
700
813
  */
814
+ /**
815
+ * @file TsConfig.Types.ts
816
+ * @author Gage Sorrell <gage@sorrell.sh>
817
+ * @copyright (c) 2026 Gage Sorrell
818
+ * @license MIT
819
+ */
701
820
  //# sourceMappingURL=index.cjs.map