@ac-kit/node 0.1.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.
Files changed (139) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +39 -0
  3. package/dist/child-process/exec-async.d.ts +24 -0
  4. package/dist/child-process/exec-async.d.ts.map +1 -0
  5. package/dist/child-process/exec-async.js +33 -0
  6. package/dist/child-process/exec-async.js.map +1 -0
  7. package/dist/error/node-aggregate-error.d.ts +18 -0
  8. package/dist/error/node-aggregate-error.d.ts.map +1 -0
  9. package/dist/error/node-aggregate-error.js +13 -0
  10. package/dist/error/node-aggregate-error.js.map +1 -0
  11. package/dist/error/node-error.d.ts +26 -0
  12. package/dist/error/node-error.d.ts.map +1 -0
  13. package/dist/error/node-error.js +21 -0
  14. package/dist/error/node-error.js.map +1 -0
  15. package/dist/error/node-exec-error.d.ts +21 -0
  16. package/dist/error/node-exec-error.d.ts.map +1 -0
  17. package/dist/error/node-exec-error.js +17 -0
  18. package/dist/error/node-exec-error.js.map +1 -0
  19. package/dist/error/node-system-error.d.ts +46 -0
  20. package/dist/error/node-system-error.d.ts.map +1 -0
  21. package/dist/error/node-system-error.js +35 -0
  22. package/dist/error/node-system-error.js.map +1 -0
  23. package/dist/error/process-exit-error.d.ts +22 -0
  24. package/dist/error/process-exit-error.d.ts.map +1 -0
  25. package/dist/error/process-exit-error.js +46 -0
  26. package/dist/error/process-exit-error.js.map +1 -0
  27. package/dist/fs/compress-file.d.ts +42 -0
  28. package/dist/fs/compress-file.d.ts.map +1 -0
  29. package/dist/fs/compress-file.js +72 -0
  30. package/dist/fs/compress-file.js.map +1 -0
  31. package/dist/fs/create-temp-dir.d.ts +13 -0
  32. package/dist/fs/create-temp-dir.d.ts.map +1 -0
  33. package/dist/fs/create-temp-dir.js +18 -0
  34. package/dist/fs/create-temp-dir.js.map +1 -0
  35. package/dist/fs/escape-path.d.ts +19 -0
  36. package/dist/fs/escape-path.d.ts.map +1 -0
  37. package/dist/fs/escape-path.js +20 -0
  38. package/dist/fs/escape-path.js.map +1 -0
  39. package/dist/fs/exists-async.d.ts +9 -0
  40. package/dist/fs/exists-async.d.ts.map +1 -0
  41. package/dist/fs/exists-async.js +17 -0
  42. package/dist/fs/exists-async.js.map +1 -0
  43. package/dist/fs/file-content-equal.d.ts +18 -0
  44. package/dist/fs/file-content-equal.d.ts.map +1 -0
  45. package/dist/fs/file-content-equal.js +50 -0
  46. package/dist/fs/file-content-equal.js.map +1 -0
  47. package/dist/fs/is-directory-async.d.ts +11 -0
  48. package/dist/fs/is-directory-async.d.ts.map +1 -0
  49. package/dist/fs/is-directory-async.js +18 -0
  50. package/dist/fs/is-directory-async.js.map +1 -0
  51. package/dist/fs/types.d.ts +19 -0
  52. package/dist/fs/types.d.ts.map +1 -0
  53. package/dist/fs/types.js +23 -0
  54. package/dist/fs/types.js.map +1 -0
  55. package/dist/fs/walk-paths.d.ts +108 -0
  56. package/dist/fs/walk-paths.d.ts.map +1 -0
  57. package/dist/fs/walk-paths.js +197 -0
  58. package/dist/fs/walk-paths.js.map +1 -0
  59. package/dist/fs/write-file-atomic.d.ts +42 -0
  60. package/dist/fs/write-file-atomic.d.ts.map +1 -0
  61. package/dist/fs/write-file-atomic.js +126 -0
  62. package/dist/fs/write-file-atomic.js.map +1 -0
  63. package/dist/index.d.ts +34 -0
  64. package/dist/index.d.ts.map +1 -0
  65. package/dist/index.js +34 -0
  66. package/dist/index.js.map +1 -0
  67. package/dist/module/import-module.d.ts +13 -0
  68. package/dist/module/import-module.d.ts.map +1 -0
  69. package/dist/module/import-module.js +18 -0
  70. package/dist/module/import-module.js.map +1 -0
  71. package/dist/module/optional-import.d.ts +19 -0
  72. package/dist/module/optional-import.d.ts.map +1 -0
  73. package/dist/module/optional-import.js +30 -0
  74. package/dist/module/optional-import.js.map +1 -0
  75. package/dist/module/resolve-module.d.ts +13 -0
  76. package/dist/module/resolve-module.d.ts.map +1 -0
  77. package/dist/module/resolve-module.js +26 -0
  78. package/dist/module/resolve-module.js.map +1 -0
  79. package/dist/net/dgram-socket.d.ts +235 -0
  80. package/dist/net/dgram-socket.d.ts.map +1 -0
  81. package/dist/net/dgram-socket.js +341 -0
  82. package/dist/net/dgram-socket.js.map +1 -0
  83. package/dist/net/ipc-socket.d.ts +34 -0
  84. package/dist/net/ipc-socket.d.ts.map +1 -0
  85. package/dist/net/ipc-socket.js +63 -0
  86. package/dist/net/ipc-socket.js.map +1 -0
  87. package/dist/net/stream-socket.d.ts +156 -0
  88. package/dist/net/stream-socket.d.ts.map +1 -0
  89. package/dist/net/stream-socket.js +188 -0
  90. package/dist/net/stream-socket.js.map +1 -0
  91. package/dist/net/tcp-server.d.ts +110 -0
  92. package/dist/net/tcp-server.d.ts.map +1 -0
  93. package/dist/net/tcp-server.js +198 -0
  94. package/dist/net/tcp-server.js.map +1 -0
  95. package/dist/net/tcp-socket.d.ts +87 -0
  96. package/dist/net/tcp-socket.d.ts.map +1 -0
  97. package/dist/net/tcp-socket.js +153 -0
  98. package/dist/net/tcp-socket.js.map +1 -0
  99. package/dist/net/tls-socket.d.ts +45 -0
  100. package/dist/net/tls-socket.d.ts.map +1 -0
  101. package/dist/net/tls-socket.js +148 -0
  102. package/dist/net/tls-socket.js.map +1 -0
  103. package/dist/process/fork-process.d.ts +63 -0
  104. package/dist/process/fork-process.d.ts.map +1 -0
  105. package/dist/process/fork-process.js +98 -0
  106. package/dist/process/fork-process.js.map +1 -0
  107. package/dist/process/guarded.d.ts +12 -0
  108. package/dist/process/guarded.d.ts.map +1 -0
  109. package/dist/process/guarded.js +25 -0
  110. package/dist/process/guarded.js.map +1 -0
  111. package/dist/process/spawn-process.d.ts +37 -0
  112. package/dist/process/spawn-process.d.ts.map +1 -0
  113. package/dist/process/spawn-process.js +73 -0
  114. package/dist/process/spawn-process.js.map +1 -0
  115. package/dist/process/spawned-process.d.ts +102 -0
  116. package/dist/process/spawned-process.d.ts.map +1 -0
  117. package/dist/process/spawned-process.js +226 -0
  118. package/dist/process/spawned-process.js.map +1 -0
  119. package/dist/process/start-process.d.ts +54 -0
  120. package/dist/process/start-process.d.ts.map +1 -0
  121. package/dist/process/start-process.js +73 -0
  122. package/dist/process/start-process.js.map +1 -0
  123. package/dist/shell/escape-command-arg.d.ts +9 -0
  124. package/dist/shell/escape-command-arg.d.ts.map +1 -0
  125. package/dist/shell/escape-command-arg.js +13 -0
  126. package/dist/shell/escape-command-arg.js.map +1 -0
  127. package/dist/shell/escape-command.d.ts +10 -0
  128. package/dist/shell/escape-command.d.ts.map +1 -0
  129. package/dist/shell/escape-command.js +12 -0
  130. package/dist/shell/escape-command.js.map +1 -0
  131. package/dist/stream/non-closing-writable-stream.d.ts +14 -0
  132. package/dist/stream/non-closing-writable-stream.d.ts.map +1 -0
  133. package/dist/stream/non-closing-writable-stream.js +20 -0
  134. package/dist/stream/non-closing-writable-stream.js.map +1 -0
  135. package/dist/stream/write-async.d.ts +12 -0
  136. package/dist/stream/write-async.d.ts.map +1 -0
  137. package/dist/stream/write-async.js +22 -0
  138. package/dist/stream/write-async.js.map +1 -0
  139. package/package.json +60 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape-path.d.ts","sourceRoot":"","sources":["../../src/fs/escape-path.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAEzE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,EAAE,YACqC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import os from "node:os";
2
+ import { escapePosixPath, escapeWin32Path } from "@ac-kit/core";
3
+ /**
4
+ * Escapes a string to be used as a safe file or directory name.
5
+ *
6
+ * It replaces any characters that are not allowed in file or directory names
7
+ * with an underscore (_). It also ensures that the name does not exceed the
8
+ * maximum length allowed by the operating system (255 characters).
9
+ *
10
+ * On POSIX systems, it replaces / and null characters. On Windows systems, it
11
+ * replaces < > : " / \ | ? * and control characters (0-31). It also avoids
12
+ * reserved names like CON, PRN, AUX, NUL, COM1, LPT1, etc.
13
+ *
14
+ * @param str The string to escape
15
+ * @returns The escaped string
16
+ * @see https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names
17
+ * @see https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits
18
+ */
19
+ export const escapePath = os.platform() === "win32" ? escapeWin32Path : escapePosixPath;
20
+ //# sourceMappingURL=escape-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escape-path.js","sourceRoot":"","sources":["../../src/fs/escape-path.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAEzB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAIhE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,UAAU,GACtB,EAAE,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { access } from "node:fs/promises";
2
+ /**
3
+ * Tests whether the given file is visible by the calling process.
4
+ *
5
+ * @param path The file path to check for existence.
6
+ * @returns True if the file is visible by the calling process, false otherwise.
7
+ */
8
+ export declare function existsAsync(path: Parameters<typeof access>[0]): Promise<boolean>;
9
+ //# sourceMappingURL=exists-async.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exists-async.d.ts","sourceRoot":"","sources":["../../src/fs/exists-async.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,MAAM,kBAAkB,CAAC;AAErD;;;;;GAKG;AACH,wBAAsB,WAAW,CAChC,IAAI,EAAE,UAAU,CAAC,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,GAChC,OAAO,CAAC,OAAO,CAAC,CAOlB"}
@@ -0,0 +1,17 @@
1
+ import { access, constants } from "node:fs/promises";
2
+ /**
3
+ * Tests whether the given file is visible by the calling process.
4
+ *
5
+ * @param path The file path to check for existence.
6
+ * @returns True if the file is visible by the calling process, false otherwise.
7
+ */
8
+ export async function existsAsync(path) {
9
+ try {
10
+ await access(path, constants.F_OK);
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ return true;
16
+ }
17
+ //# sourceMappingURL=exists-async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exists-async.js","sourceRoot":"","sources":["../../src/fs/exists-async.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,IAAkC;IAElC,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { PathLike } from "node:fs";
2
+ export type FileContentEqualOptions = {
3
+ /** The size of the buffer to use when reading the file (default: 64KiB) */
4
+ bufferSize?: number;
5
+ /** An AbortSignal to cancel the operation. */
6
+ signal?: AbortSignal | null;
7
+ };
8
+ /**
9
+ * Compares the content of a file with the provided data.
10
+ *
11
+ * @param path The path to the file to compare.
12
+ * @param data The data to compare with the file content.
13
+ * @param options Options for the file comparison.
14
+ * @returns True if the file content is equal to the provided data, false
15
+ * otherwise.
16
+ */
17
+ export declare function fileContentEqual(path: PathLike, data: Buffer, options?: FileContentEqualOptions): Promise<boolean>;
18
+ //# sourceMappingURL=file-content-equal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-content-equal.d.ts","sourceRoot":"","sources":["../../src/fs/file-content-equal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAKxC,MAAM,MAAM,uBAAuB,GAAG;IACrC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAC5B,CAAC;AAOF;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,uBAAuB,GAC/B,OAAO,CAAC,OAAO,CAAC,CAqDlB"}
@@ -0,0 +1,50 @@
1
+ import { open, stat } from "node:fs/promises";
2
+ import { BYTES_PER_KIB, defaults } from "@ac-kit/core";
3
+ const DEFAULT_FILE_CONTENT_EQUAL_OPTIONS = {
4
+ bufferSize: 64 * BYTES_PER_KIB,
5
+ signal: null,
6
+ };
7
+ /**
8
+ * Compares the content of a file with the provided data.
9
+ *
10
+ * @param path The path to the file to compare.
11
+ * @param data The data to compare with the file content.
12
+ * @param options Options for the file comparison.
13
+ * @returns True if the file content is equal to the provided data, false
14
+ * otherwise.
15
+ */
16
+ export async function fileContentEqual(path, data, options) {
17
+ const effectiveOptions = defaults(options, DEFAULT_FILE_CONTENT_EQUAL_OPTIONS);
18
+ if (effectiveOptions.bufferSize <= 0) {
19
+ throw new Error("The buffer size must be greater than 0.");
20
+ }
21
+ const fileStat = await stat(path);
22
+ if (fileStat.size !== data.length) {
23
+ return false;
24
+ }
25
+ if (fileStat.size === 0) {
26
+ return true;
27
+ }
28
+ const handle = await open(path, "r");
29
+ let position = 0;
30
+ try {
31
+ const buffer = Buffer.alloc(effectiveOptions.bufferSize);
32
+ do {
33
+ effectiveOptions.signal?.throwIfAborted();
34
+ const result = await handle.read(buffer, 0, buffer.length, position);
35
+ if (result.bytesRead === 0) {
36
+ break;
37
+ }
38
+ const compareResult = buffer.compare(data, position, position + result.bytesRead, 0, result.bytesRead);
39
+ if (compareResult !== 0) {
40
+ break;
41
+ }
42
+ position += result.bytesRead;
43
+ } while (position < data.length);
44
+ }
45
+ finally {
46
+ await handle.close();
47
+ }
48
+ return position === data.length;
49
+ }
50
+ //# sourceMappingURL=file-content-equal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-content-equal.js","sourceRoot":"","sources":["../../src/fs/file-content-equal.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAUvD,MAAM,kCAAkC,GAAsC;IAC7E,UAAU,EAAE,EAAE,GAAG,aAAa;IAC9B,MAAM,EAAE,IAAI;CACZ,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,IAAc,EACd,IAAY,EACZ,OAAiC;IAEjC,MAAM,gBAAgB,GAAG,QAAQ,CAChC,OAAO,EACP,kCAAkC,CAClC,CAAC;IAEF,IAAI,gBAAgB,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC;IACd,CAAC;IAED,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC;IACb,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAErC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAEzD,GAAG,CAAC;YACH,gBAAgB,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;YAE1C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAErE,IAAI,MAAM,CAAC,SAAS,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM;YACP,CAAC;YAED,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CACnC,IAAI,EACJ,QAAQ,EACR,QAAQ,GAAG,MAAM,CAAC,SAAS,EAC3B,CAAC,EACD,MAAM,CAAC,SAAS,CAChB,CAAC;YACF,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;gBACzB,MAAM;YACP,CAAC;YAED,QAAQ,IAAI,MAAM,CAAC,SAAS,CAAC;QAC9B,CAAC,QAAQ,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE;IAClC,CAAC;YAAS,CAAC;QACV,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IAED,OAAO,QAAQ,KAAK,IAAI,CAAC,MAAM,CAAC;AACjC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { stat } from "node:fs/promises";
2
+ /**
3
+ * Tests whether `path` exists and is a directory. An unreadable or missing path
4
+ * is `false`, never a throw — the caller asked a question, not for the reason
5
+ * behind the answer.
6
+ *
7
+ * @param path The path to check.
8
+ * @returns True when `path` resolves to a directory.
9
+ */
10
+ export declare function isDirectoryAsync(path: Parameters<typeof stat>[0]): Promise<boolean>;
11
+ //# sourceMappingURL=is-directory-async.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-directory-async.d.ts","sourceRoot":"","sources":["../../src/fs/is-directory-async.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC;;;;;;;GAOG;AACH,wBAAsB,gBAAgB,CACrC,IAAI,EAAE,UAAU,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,GAC9B,OAAO,CAAC,OAAO,CAAC,CAMlB"}
@@ -0,0 +1,18 @@
1
+ import { stat } from "node:fs/promises";
2
+ /**
3
+ * Tests whether `path` exists and is a directory. An unreadable or missing path
4
+ * is `false`, never a throw — the caller asked a question, not for the reason
5
+ * behind the answer.
6
+ *
7
+ * @param path The path to check.
8
+ * @returns True when `path` resolves to a directory.
9
+ */
10
+ export async function isDirectoryAsync(path) {
11
+ try {
12
+ return (await stat(path)).isDirectory();
13
+ }
14
+ catch {
15
+ return false;
16
+ }
17
+ }
18
+ //# sourceMappingURL=is-directory-async.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-directory-async.js","sourceRoot":"","sources":["../../src/fs/is-directory-async.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAExC;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACrC,IAAgC;IAEhC,IAAI,CAAC;QACJ,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { readFile, writeFile } from "node:fs/promises";
2
+ import { type SetNullable } from "@ac-kit/core";
3
+ /** Options for fs.readFile (excluding `signal`) */
4
+ export type NodeReadFileOptions = SetNullable<Extract<Parameters<typeof readFile>[1], object>, "signal">;
5
+ /** Options for fs.writeFile (excluding `signal`) */
6
+ export type NodeWriteFileOptions = SetNullable<Extract<Parameters<typeof writeFile>[2], object>, "signal">;
7
+ /**
8
+ * Default options for fs.writeFile from Node.js documentation.
9
+ *
10
+ * @see https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback
11
+ */
12
+ export declare const NODE_WRITE_FILE_DEFAULT_OPTIONS: Required<NodeWriteFileOptions>;
13
+ /**
14
+ * Default options for fs.readFile from Node.js documentation.
15
+ *
16
+ * @see https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback
17
+ */
18
+ export declare const NODE_READ_FILE_DEFAULT_OPTIONS: Required<NodeReadFileOptions>;
19
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/fs/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE5D,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,mDAAmD;AACnD,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAC5C,OAAO,CAAC,UAAU,CAAC,OAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAC/C,QAAQ,CACR,CAAC;AAEF,oDAAoD;AACpD,MAAM,MAAM,oBAAoB,GAAG,WAAW,CAC7C,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAChD,QAAQ,CACR,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,+BAA+B,EAAE,QAAQ,CAAC,oBAAoB,CAM1E,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,mBAAmB,CAIxE,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Default options for fs.writeFile from Node.js documentation.
3
+ *
4
+ * @see https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback
5
+ */
6
+ export const NODE_WRITE_FILE_DEFAULT_OPTIONS = {
7
+ encoding: null,
8
+ mode: 0o666,
9
+ flag: "w",
10
+ flush: false,
11
+ signal: null,
12
+ };
13
+ /**
14
+ * Default options for fs.readFile from Node.js documentation.
15
+ *
16
+ * @see https://nodejs.org/api/fs.html#fs_fs_readfile_path_options_callback
17
+ */
18
+ export const NODE_READ_FILE_DEFAULT_OPTIONS = {
19
+ encoding: null,
20
+ flag: "r",
21
+ signal: null,
22
+ };
23
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/fs/types.ts"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,MAAM,CAAC,MAAM,+BAA+B,GAAmC;IAC9E,QAAQ,EAAE,IAAI;IACd,IAAI,EAAE,KAAK;IACX,IAAI,EAAE,GAAG;IACT,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE,IAAI;CACZ,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAkC;IAC5E,QAAQ,EAAE,IAAI;IACd,IAAI,EAAE,GAAG;IACT,MAAM,EAAE,IAAI;CACZ,CAAC"}
@@ -0,0 +1,108 @@
1
+ import type { Dirent } from "node:fs";
2
+ /**
3
+ * Selects paths during a walk.
4
+ *
5
+ * A glob and a regular expression are both tested against the `/`-separated
6
+ * path relative to the walk's root; a predicate additionally receives the
7
+ * directory entry, whose kind is already known and costs no extra syscall.
8
+ *
9
+ * Only a glob carries depth information. That is what lets
10
+ * {@link WalkPathsOptions.descend} default itself, and it is why an `include`
11
+ * predicate costs a full walk: a predicate cannot be asked whether anything
12
+ * under a directory could match, so nothing can be pruned on its behalf.
13
+ */
14
+ export type PathFilter = string | RegExp | ((entry: Dirent, path: string) => boolean);
15
+ /** What a walk does when a directory cannot be read. */
16
+ export type UnreadablePolicy =
17
+ /** Any unreadable directory aborts the walk. */
18
+ "throw"
19
+ /** The root must be readable; an unreadable directory below it is skipped. */
20
+ | "require-root"
21
+ /** Nothing throws — an unreadable root simply yields nothing. */
22
+ | "skip";
23
+ export type WalkPathsOptions = {
24
+ /** Directory the walk starts from, and paths are relative to. Default `"."`. */
25
+ readonly root?: string | null;
26
+ /** Yields only the paths matching one of these. Default: every path. */
27
+ readonly include?: PathFilter | readonly PathFilter[] | null;
28
+ /**
29
+ * Drops the matching paths, and never descends into a matching directory.
30
+ * This is the pruning knob: an excluded subtree costs nothing.
31
+ */
32
+ readonly exclude?: PathFilter | readonly PathFilter[] | null;
33
+ /**
34
+ * Which directories to descend into.
35
+ *
36
+ * Defaults to `true` when any `include` glob contains `**`, and to `false`
37
+ * otherwise — a regular expression or a predicate says nothing about depth,
38
+ * so it stays flat. An explicit value always wins over that default.
39
+ */
40
+ readonly descend?: boolean | PathFilter | null;
41
+ /** Hard depth cap; `1` is the root's own entries. Default: unbounded. */
42
+ readonly maxDepth?: number | null;
43
+ /**
44
+ * Descends through symbolic links to directories. Default `false`.
45
+ *
46
+ * Enabling it costs a `stat` and a `realpath` per symlink encountered, which
47
+ * is what makes a link back to an ancestor detectable instead of endless.
48
+ */
49
+ readonly followSymlinks?: boolean | null;
50
+ /** Default `"require-root"`. */
51
+ readonly unreadable?: UnreadablePolicy | null;
52
+ readonly signal?: AbortSignal | null;
53
+ };
54
+ export type WalkedPath = {
55
+ /** Path relative to the root, always `/`-separated. */
56
+ path: string;
57
+ entry: Dirent;
58
+ };
59
+ /**
60
+ * Streams the paths under a directory, filtered by globs, regular expressions
61
+ * or predicates.
62
+ *
63
+ * Paths are yielded relative to the root, `/`-separated on every platform, and
64
+ * sorted within each directory. Because one traversal tests every filter, a
65
+ * path is yielded once no matter how many patterns it matches — no
66
+ * deduplication needed. Stop early by breaking out of the loop; the generator's
67
+ * cleanup runs as usual.
68
+ *
69
+ * Depth comes from the patterns, as it does in a shell: `"*.ts"` stays flat,
70
+ * `"**\/*.ts"` recurses. {@link WalkPathsOptions.descend} overrides that, and
71
+ * is the only way to recurse with a regular expression or a predicate.
72
+ *
73
+ * Time complexity: one `readdir` per directory descended into, and one filter
74
+ * test per entry visited. `exclude` is applied before descending, so an
75
+ * excluded subtree is never read.
76
+ *
77
+ * @example
78
+ * Every `.bench.ts` outside `node_modules`
79
+ * ```ts
80
+ * for await (const { path } of walkPaths({
81
+ * include: "**\/*.bench.ts",
82
+ * exclude: "**\/node_modules/**",
83
+ * })) {
84
+ * console.log(path);
85
+ * }
86
+ * ```
87
+ *
88
+ * @example
89
+ * The CPU directories of sysfs, tolerating an absent tree
90
+ * ```ts
91
+ * const names = await Array.fromAsync(
92
+ * walkPaths({
93
+ * root: "/sys/devices/system/cpu",
94
+ * include: /^cpu\d+$/,
95
+ * unreadable: "skip",
96
+ * }),
97
+ * (walked) => walked.path,
98
+ * );
99
+ * ```
100
+ *
101
+ * @param options Root, filters, depth, symlink and error handling. See
102
+ * {@link WalkPathsOptions}.
103
+ * @returns The matching paths, each with the directory entry it came from.
104
+ * @throws When a directory cannot be read and
105
+ * {@link WalkPathsOptions.unreadable} does not tolerate it.
106
+ */
107
+ export declare function walkPaths(options?: WalkPathsOptions): AsyncGenerator<WalkedPath>;
108
+ //# sourceMappingURL=walk-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk-paths.d.ts","sourceRoot":"","sources":["../../src/fs/walk-paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAItC;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GACnB,MAAM,GACN,MAAM,GACN,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;AAE9C,wDAAwD;AACxD,MAAM,MAAM,gBAAgB;AAC3B,gDAAgD;AAC9C,OAAO;AACT,8EAA8E;GAC5E,cAAc;AAChB,iEAAiE;GAC/D,MAAM,CAAC;AAEV,MAAM,MAAM,gBAAgB,GAAG;IAC9B,gFAAgF;IAChF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE9B,wEAAwE;IACxE,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE,GAAG,IAAI,CAAC;IAE7D;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,SAAS,UAAU,EAAE,GAAG,IAAI,CAAC;IAE7D;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;IAE/C,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAElC;;;;;OAKG;IACH,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAEzC,gCAAgC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;IAE9C,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACxB,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IAEb,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAyLF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,wBAAuB,SAAS,CAC/B,OAAO,CAAC,EAAE,gBAAgB,GACxB,cAAc,CAAC,UAAU,CAAC,CAmC5B"}
@@ -0,0 +1,197 @@
1
+ import { readdir, realpath, stat } from "node:fs/promises";
2
+ import { join, matchesGlob, resolve, sep } from "node:path";
3
+ function matchesFilter_(filter, entry, path) {
4
+ if (typeof filter === "function") {
5
+ return filter(entry, path);
6
+ }
7
+ if (typeof filter === "string") {
8
+ return matchesGlob(path, filter);
9
+ }
10
+ // `search` over `test`: it ignores `lastIndex`, so a caller's `/g` pattern
11
+ // cannot make the filter stateful.
12
+ return path.search(filter) !== -1;
13
+ }
14
+ function matchesAny_(filters, entry, path) {
15
+ for (let index = 0; index < filters.length; index++) {
16
+ if (matchesFilter_(filters[index], entry, path)) {
17
+ return true;
18
+ }
19
+ }
20
+ return false;
21
+ }
22
+ function toFilterList_(filter) {
23
+ if (filter === null || filter === undefined) {
24
+ return [];
25
+ }
26
+ if (typeof filter === "string" ||
27
+ typeof filter === "function" ||
28
+ filter instanceof RegExp) {
29
+ return [filter];
30
+ }
31
+ return filter;
32
+ }
33
+ function compareByName_(left, right) {
34
+ if (left.name < right.name) {
35
+ return -1;
36
+ }
37
+ return left.name > right.name ? 1 : 0;
38
+ }
39
+ /** `null` when the directory is unreadable and the policy tolerates it. */
40
+ async function readEntries_(context, relativeDirectory) {
41
+ const isRoot = relativeDirectory === "";
42
+ try {
43
+ const entries = await readdir(isRoot ? context.root : join(context.root, relativeDirectory), { withFileTypes: true });
44
+ // Sorted per directory so a walk is reproducible across filesystems.
45
+ return entries.sort(compareByName_);
46
+ }
47
+ catch (error) {
48
+ if (context.unreadable === "throw" ||
49
+ (context.unreadable === "require-root" && isRoot)) {
50
+ throw error;
51
+ }
52
+ return null;
53
+ }
54
+ }
55
+ function shouldDescend_(context, entry, path) {
56
+ if (typeof context.descend === "boolean") {
57
+ return context.descend;
58
+ }
59
+ return matchesFilter_(context.descend, entry, path);
60
+ }
61
+ /**
62
+ * The resolved path to descend into, or `null` when the entry is not a
63
+ * directory, is a link that would loop, or cannot be inspected.
64
+ */
65
+ async function resolveChildDirectory_(context, entry, path, resolvedDirectory) {
66
+ if (!entry.isSymbolicLink()) {
67
+ return entry.isDirectory() ? join(resolvedDirectory, entry.name) : null;
68
+ }
69
+ if (!context.followSymlinks) {
70
+ return null;
71
+ }
72
+ const absolute = join(context.root, path);
73
+ let target;
74
+ try {
75
+ if (!(await stat(absolute)).isDirectory()) {
76
+ return null;
77
+ }
78
+ target = await realpath(absolute);
79
+ }
80
+ catch (error) {
81
+ if (context.unreadable === "throw") {
82
+ throw error;
83
+ }
84
+ return null;
85
+ }
86
+ // A link resolving to an ancestor would walk its own subtree forever.
87
+ if (resolvedDirectory === target ||
88
+ resolvedDirectory.startsWith(target + sep)) {
89
+ return null;
90
+ }
91
+ return target;
92
+ }
93
+ async function* walkDirectory_(context, relativeDirectory, resolvedDirectory, depth) {
94
+ const entries = await readEntries_(context, relativeDirectory);
95
+ if (entries === null) {
96
+ return;
97
+ }
98
+ const prefix = relativeDirectory === "" ? "" : `${relativeDirectory}/`;
99
+ for (const entry of entries) {
100
+ context.signal?.throwIfAborted();
101
+ const path = prefix + entry.name;
102
+ if (matchesAny_(context.exclude, entry, path)) {
103
+ continue;
104
+ }
105
+ if (context.include.length === 0 ||
106
+ matchesAny_(context.include, entry, path)) {
107
+ yield { path, entry };
108
+ }
109
+ if (depth >= context.maxDepth || !shouldDescend_(context, entry, path)) {
110
+ continue;
111
+ }
112
+ const child = await resolveChildDirectory_(context, entry, path, resolvedDirectory);
113
+ if (child !== null) {
114
+ yield* walkDirectory_(context, path, child, depth + 1);
115
+ }
116
+ }
117
+ }
118
+ /**
119
+ * Streams the paths under a directory, filtered by globs, regular expressions
120
+ * or predicates.
121
+ *
122
+ * Paths are yielded relative to the root, `/`-separated on every platform, and
123
+ * sorted within each directory. Because one traversal tests every filter, a
124
+ * path is yielded once no matter how many patterns it matches — no
125
+ * deduplication needed. Stop early by breaking out of the loop; the generator's
126
+ * cleanup runs as usual.
127
+ *
128
+ * Depth comes from the patterns, as it does in a shell: `"*.ts"` stays flat,
129
+ * `"**\/*.ts"` recurses. {@link WalkPathsOptions.descend} overrides that, and
130
+ * is the only way to recurse with a regular expression or a predicate.
131
+ *
132
+ * Time complexity: one `readdir` per directory descended into, and one filter
133
+ * test per entry visited. `exclude` is applied before descending, so an
134
+ * excluded subtree is never read.
135
+ *
136
+ * @example
137
+ * Every `.bench.ts` outside `node_modules`
138
+ * ```ts
139
+ * for await (const { path } of walkPaths({
140
+ * include: "**\/*.bench.ts",
141
+ * exclude: "**\/node_modules/**",
142
+ * })) {
143
+ * console.log(path);
144
+ * }
145
+ * ```
146
+ *
147
+ * @example
148
+ * The CPU directories of sysfs, tolerating an absent tree
149
+ * ```ts
150
+ * const names = await Array.fromAsync(
151
+ * walkPaths({
152
+ * root: "/sys/devices/system/cpu",
153
+ * include: /^cpu\d+$/,
154
+ * unreadable: "skip",
155
+ * }),
156
+ * (walked) => walked.path,
157
+ * );
158
+ * ```
159
+ *
160
+ * @param options Root, filters, depth, symlink and error handling. See
161
+ * {@link WalkPathsOptions}.
162
+ * @returns The matching paths, each with the directory entry it came from.
163
+ * @throws When a directory cannot be read and
164
+ * {@link WalkPathsOptions.unreadable} does not tolerate it.
165
+ */
166
+ export async function* walkPaths(options) {
167
+ const include = toFilterList_(options?.include);
168
+ const root = resolve(options?.root ?? ".");
169
+ const followSymlinks = options?.followSymlinks ?? false;
170
+ const unreadable = options?.unreadable ?? "require-root";
171
+ const context = {
172
+ root,
173
+ include,
174
+ exclude: toFilterList_(options?.exclude),
175
+ descend: options?.descend ??
176
+ include.some((filter) => typeof filter === "string" && filter.includes("**")),
177
+ maxDepth: options?.maxDepth ?? Number.POSITIVE_INFINITY,
178
+ followSymlinks,
179
+ unreadable,
180
+ signal: options?.signal ?? null,
181
+ };
182
+ // The ancestor check compares resolved paths, so the root has to be one too.
183
+ let resolvedRoot = root;
184
+ if (followSymlinks) {
185
+ try {
186
+ resolvedRoot = await realpath(root);
187
+ }
188
+ catch (error) {
189
+ if (unreadable !== "skip") {
190
+ throw error;
191
+ }
192
+ return;
193
+ }
194
+ }
195
+ yield* walkDirectory_(context, "", resolvedRoot, 1);
196
+ }
197
+ //# sourceMappingURL=walk-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"walk-paths.js","sourceRoot":"","sources":["../../src/fs/walk-paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import { type PathLike } from "node:fs";
2
+ import { writeFile } from "node:fs/promises";
3
+ import { type NodeWriteFileOptions } from "./types.js";
4
+ /**
5
+ * Replaces a file's contents in one step: a reader sees either the previous
6
+ * contents or the new ones, never a partial write.
7
+ *
8
+ * The data goes to a uniquely named temporary file beside the destination,
9
+ * which is then `rename`d over it. `rename` replaces the destination
10
+ * atomically, so no window exists in which the path is missing.
11
+ *
12
+ * Appending is rejected — `flag: "a"` and `O_APPEND` throw. Staging an append
13
+ * means copying the destination into the temporary file and appending there,
14
+ * which silently discards whatever another process wrote in between: the
15
+ * opposite of what an appender expects. Read, concatenate and replace instead,
16
+ * serialising the writers yourself when there is more than one:
17
+ *
18
+ * ```ts
19
+ * const current = await readFile(path, "utf8").catch(() => "");
20
+ * await writeFileAtomic(path, `${current}${line}\n`);
21
+ * ```
22
+ *
23
+ * When the destination already exists its permission bits carry over, unless
24
+ * `options.mode` says otherwise. Ownership does not: the replacement belongs to
25
+ * the calling user.
26
+ *
27
+ * Durability is a separate concern from atomicity — pass `flush: true` to fsync
28
+ * the data before the rename. The parent directory is never fsynced, so the
29
+ * rename itself may still be lost to a power failure.
30
+ *
31
+ * @param filePath The path to replace.
32
+ * @param data The contents to write.
33
+ * @param options Encoding, mode, flag, flush and abort signal, as for
34
+ * `fs.writeFile`. `flag` may only select exclusive creation (`"wx"`,
35
+ * `O_EXCL`); appending throws.
36
+ * @returns A promise that resolves once the destination holds the new contents.
37
+ * @throws {TypeError} When `options.flag` requests an append.
38
+ * @throws When the destination exists and `options.flag` is exclusive (`code:
39
+ * "EEXIST"`), or when any underlying syscall fails.
40
+ */
41
+ export declare function writeFileAtomic(filePath: PathLike, data: Parameters<typeof writeFile>[1], options?: NodeWriteFileOptions): ReturnType<typeof writeFile>;
42
+ //# sourceMappingURL=write-file-atomic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-file-atomic.d.ts","sourceRoot":"","sources":["../../src/fs/write-file-atomic.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,QAAQ,EAAc,MAAM,SAAS,CAAC;AAC1E,OAAO,EAA+B,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAM1E,OAAO,EAEN,KAAK,oBAAoB,EACzB,MAAM,YAAY,CAAC;AAwDpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,eAAe,CACpC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,EACrC,OAAO,CAAC,EAAE,oBAAoB,GAC5B,UAAU,CAAC,OAAO,SAAS,CAAC,CAgD9B"}