@thi.ng/file-io 2.1.37 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-05-28T12:02:39Z
3
+ - **Last updated**: 2025-07-03T15:03:31Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -11,6 +11,16 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
11
11
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
12
12
  and/or version bumps of transitive dependencies.
13
13
 
14
+ ## [2.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/file-io@2.2.0) (2025-07-03)
15
+
16
+ #### 🚀 Features
17
+
18
+ - add makedPath() and mask registry fns ([74796f8](https://github.com/thi-ng/umbrella/commit/74796f8))
19
+ - add copyFile() fns (incl. async version) ([19ad5f3](https://github.com/thi-ng/umbrella/commit/19ad5f3))
20
+ - update logging calls to use masked paths only ([c25a2cb](https://github.com/thi-ng/umbrella/commit/c25a2cb))
21
+ - add async versions of various file fns ([eff47b7](https://github.com/thi-ng/umbrella/commit/eff47b7))
22
+ - add deleteFiles() and async versions ([1185911](https://github.com/thi-ng/umbrella/commit/1185911))
23
+
14
24
  ### [2.1.21](https://github.com/thi-ng/umbrella/tree/@thi.ng/file-io@2.1.21) (2025-01-14)
15
25
 
16
26
  #### ♻️ Refactoring
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Mastodon Follow](https://img.shields.io/mastodon/follow/109331703950160316?domain=https%3A%2F%2Fmastodon.thi.ng&style=social)](https://mastodon.thi.ng/@toxi)
8
8
 
9
9
  > [!NOTE]
10
- > This is one of 208 standalone projects, maintained as part
10
+ > This is one of 209 standalone projects, maintained as part
11
11
  > of the [@thi.ng/umbrella](https://github.com/thi-ng/umbrella/) monorepo
12
12
  > and anti-framework.
13
13
  >
@@ -15,6 +15,10 @@
15
15
  > GitHub](https://github.com/sponsors/postspectacular). Thank you! ❤️
16
16
 
17
17
  - [About](#about)
18
+ - [Available functions](#available-functions)
19
+ - [Files](#files)
20
+ - [Directories](#directories)
21
+ - [Logging](#logging)
18
22
  - [Status](#status)
19
23
  - [Installation](#installation)
20
24
  - [Dependencies](#dependencies)
@@ -25,12 +29,58 @@
25
29
 
26
30
  ## About
27
31
 
28
- Assorted file I/O utils (with logging support) for NodeJS/Bun.
32
+ Assorted file I/O utils (w/ logging support) for NodeJS/Bun.
29
33
 
30
34
  Most functions in this package have optional support for the
31
35
  [`ILogger`](https://github.com/thi-ng/umbrella/tree/develop/packages/logger)
32
36
  logging interface.
33
37
 
38
+ ## Available functions
39
+
40
+ ### Files
41
+
42
+ - [bufferHash](https://docs.thi.ng/umbrella/file-io/functions/bufferHash.html)
43
+ - [copyFile](https://docs.thi.ng/umbrella/file-io/functions/copyFile.html)
44
+ - [copyFileAsync](https://docs.thi.ng/umbrella/file-io/functions/copyFileAsync.html)
45
+ - [createTempFile](https://docs.thi.ng/umbrella/file-io/functions/createTempFile.html)
46
+ - [deleteFile](https://docs.thi.ng/umbrella/file-io/functions/deleteFile.html)
47
+ - [deleteFileAsync](https://docs.thi.ng/umbrella/file-io/functions/deleteFileAsync.html)
48
+ - [deleteFiles](https://docs.thi.ng/umbrella/file-io/functions/deleteFiles.html)
49
+ - [deleteFilesAsync](https://docs.thi.ng/umbrella/file-io/functions/deleteFilesAsync.html)
50
+ - [files](https://docs.thi.ng/umbrella/file-io/functions/files.html)
51
+ - [fileChunks](https://docs.thi.ng/umbrella/file-io/functions/fileChunks.html)
52
+ - [fileExt](https://docs.thi.ng/umbrella/file-io/functions/fileExt.html)
53
+ - [fileHash](https://docs.thi.ng/umbrella/file-io/functions/fileHash.html)
54
+ - [fileWatcher](https://docs.thi.ng/umbrella/file-io/functions/fileWatcher.html)
55
+ - [readBinary](https://docs.thi.ng/umbrella/file-io/functions/readBinary.html)
56
+ - [readBinaryAsync](https://docs.thi.ng/umbrella/file-io/functions/readBinaryAsync.html)
57
+ - [readJSON](https://docs.thi.ng/umbrella/file-io/functions/readJSON.html)
58
+ - [readJSONAsync](https://docs.thi.ng/umbrella/file-io/functions/readJSONAsync.html)
59
+ - [readText](https://docs.thi.ng/umbrella/file-io/functions/readText.html)
60
+ - [readTextAsync](https://docs.thi.ng/umbrella/file-io/functions/readTextAsync.html)
61
+ - [streamHash](https://docs.thi.ng/umbrella/file-io/functions/streamHash.html)
62
+ - [tempFilePath](https://docs.thi.ng/umbrella/file-io/functions/tempFilePath.html)
63
+ - [writeFile](https://docs.thi.ng/umbrella/file-io/functions/writeFile.html)
64
+ - [writeFileAsync](https://docs.thi.ng/umbrella/file-io/functions/writeFileAsync.html)
65
+ - [writeJSON](https://docs.thi.ng/umbrella/file-io/functions/writeJSON.html)
66
+ - [writeJSONAsync](https://docs.thi.ng/umbrella/file-io/functions/writeJSONAsync.html)
67
+ - [writeText](https://docs.thi.ng/umbrella/file-io/functions/writeText.html)
68
+ - [writeTextAsync](https://docs.thi.ng/umbrella/file-io/functions/writeTextAsync.html)
69
+
70
+ ### Directories
71
+
72
+ - [deleteDir](https://docs.thi.ng/umbrella/file-io/functions/deleteDir.html)
73
+ - [dirs](https://docs.thi.ng/umbrella/file-io/functions/dirs.html)
74
+ - [ensureDir](https://docs.thi.ng/umbrella/file-io/functions/ensureDir.html)
75
+ - [ensureDirForFile](https://docs.thi.ng/umbrella/file-io/functions/ensureDirForFile.html)
76
+ - [isDirectory](https://docs.thi.ng/umbrella/file-io/functions/isDirectory.html)
77
+
78
+ ### Logging
79
+
80
+ - [maskedPath](https://docs.thi.ng/umbrella/file-io/functions/maskedPath.html)
81
+ - [addPathMask](https://docs.thi.ng/umbrella/file-io/functions/addPathMask.html)
82
+ - [setPathMasks](https://docs.thi.ng/umbrella/file-io/functions/setPathMasks.html)
83
+
34
84
  ## Status
35
85
 
36
86
  **STABLE** - used in production
@@ -55,7 +105,7 @@ For Node.js REPL:
55
105
  const fio = await import("@thi.ng/file-io");
56
106
  ```
57
107
 
58
- Package sizes (brotli'd, pre-treeshake): ESM: 2.05 KB
108
+ Package sizes (brotli'd, pre-treeshake): ESM: 2.44 KB
59
109
 
60
110
  ## Dependencies
61
111
 
package/copy.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { ILogger } from "@thi.ng/logger";
2
+ export declare const copyFile: (src: string, dest: string, logger?: ILogger) => void;
3
+ export declare const copyFileAsync: (src: string, dest: string, logger?: ILogger) => Promise<void>;
4
+ //# sourceMappingURL=copy.d.ts.map
package/copy.js ADDED
@@ -0,0 +1,15 @@
1
+ import { copyFileSync } from "node:fs";
2
+ import { copyFile as $copy } from "node:fs/promises";
3
+ import { maskedPath } from "./mask.js";
4
+ const copyFile = (src, dest, logger) => {
5
+ logger?.debug(maskedPath(`copying file: ${src} \u2192 ${dest}`));
6
+ copyFileSync(src, dest);
7
+ };
8
+ const copyFileAsync = (src, dest, logger) => {
9
+ logger?.debug(maskedPath(`copying file: ${src} \u2192 ${dest}`));
10
+ return $copy(src, dest);
11
+ };
12
+ export {
13
+ copyFile,
14
+ copyFileAsync
15
+ };
package/delete.d.ts CHANGED
@@ -9,6 +9,17 @@ import type { ILogger } from "@thi.ng/logger";
9
9
  * @param dryRun
10
10
  */
11
11
  export declare const deleteFile: (path: string, logger?: ILogger, dryRun?: boolean) => void;
12
+ export declare const deleteFileAsync: (path: string, logger?: ILogger, dryRun?: boolean) => Promise<void>;
13
+ /**
14
+ * Deletes multiple files via {@link deleteFile}. Any errors are caught and
15
+ * logged, but ignored otherwise. If `dryRun` is true, files WON'T be deleted.
16
+ *
17
+ * @param paths
18
+ * @param logger
19
+ * @param dryRun
20
+ */
21
+ export declare const deleteFiles: (paths: Iterable<string>, logger?: ILogger, dryRun?: boolean) => void;
22
+ export declare const deleteFilesAsync: (paths: Iterable<string>, logger?: ILogger, dryRun?: boolean) => Promise<void>;
12
23
  /**
13
24
  * Like {@link deleteFile}, but attempts to recursively remove an entire
14
25
  * directory at given path.
package/delete.js CHANGED
@@ -1,15 +1,53 @@
1
1
  import { rmSync, unlinkSync } from "node:fs";
2
+ import { unlink } from "node:fs/promises";
3
+ import { maskedPath } from "./mask.js";
2
4
  const deleteFile = (path, logger, dryRun = false) => {
3
- logger?.info(`${dryRun ? "[dryrun] " : ""}deleting file: ${path}`);
5
+ logger?.info(
6
+ `${dryRun ? "[dryrun] " : ""}deleting file:`,
7
+ maskedPath(path)
8
+ );
4
9
  if (dryRun) return;
5
10
  unlinkSync(path);
6
11
  };
12
+ const deleteFileAsync = async (path, logger, dryRun = false) => {
13
+ logger?.info(
14
+ `${dryRun ? "[dryrun] " : ""}deleting file:`,
15
+ maskedPath(path)
16
+ );
17
+ if (dryRun) return;
18
+ return unlink(path);
19
+ };
20
+ const deleteFiles = (paths, logger, dryRun = false) => {
21
+ for (let path of paths) {
22
+ try {
23
+ deleteFile(path, logger, dryRun);
24
+ } catch (e) {
25
+ logger?.warn(
26
+ "error deleting file:",
27
+ maskedPath(e.message)
28
+ );
29
+ }
30
+ }
31
+ };
32
+ const deleteFilesAsync = async (paths, logger, dryRun = false) => {
33
+ const promises = [];
34
+ for (let path of paths) {
35
+ promises.push(deleteFileAsync(path, logger, dryRun));
36
+ }
37
+ await Promise.allSettled(promises);
38
+ };
7
39
  const deleteDir = (path, logger, dryRun = false) => {
8
- logger?.info(`${dryRun ? "[dryrun] " : ""}deleting file: ${path}`);
40
+ logger?.info(
41
+ `${dryRun ? "[dryrun] " : ""}deleting directory:`,
42
+ maskedPath(path)
43
+ );
9
44
  if (dryRun) return;
10
45
  rmSync(path, { recursive: true, force: true });
11
46
  };
12
47
  export {
13
48
  deleteDir,
14
- deleteFile
49
+ deleteFile,
50
+ deleteFileAsync,
51
+ deleteFiles,
52
+ deleteFilesAsync
15
53
  };
package/ext.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Returns file extension of given file `path`.
2
+ * Returns file extension (always lowercase) of given file `path`.
3
3
  *
4
4
  * @param path
5
5
  */
package/file-chunks.js CHANGED
@@ -1,16 +1,18 @@
1
1
  import { U32 } from "@thi.ng/hex";
2
2
  import { open } from "node:fs/promises";
3
+ import { maskedPath } from "./mask.js";
3
4
  async function* fileChunks(path, opts = {}) {
4
5
  let { size = 1024, start = 0, end = Infinity, logger } = opts;
5
- logger && logger.debug(`start reading file chunks (size: 0x${size}): ${path}`);
6
+ const mpath = maskedPath(path);
7
+ logger?.debug(`start reading file chunks (size: 0x${size}):`, mpath);
6
8
  let fd = void 0;
7
9
  try {
8
10
  fd = await open(path, "r");
9
11
  while (start < end) {
10
- logger && logger.debug(
12
+ logger?.debug(
11
13
  `reading chunk: 0x${U32(start)} - 0x${U32(
12
14
  start + size - 1
13
- )} (${path})`
15
+ )} (${mpath})`
14
16
  );
15
17
  const { buffer, bytesRead } = await fd.read({
16
18
  buffer: Buffer.alloc(size),
package/files.js CHANGED
@@ -2,6 +2,7 @@ import { readdirSync } from "node:fs";
2
2
  import { sep } from "node:path";
3
3
  import { isDirectory } from "./dir.js";
4
4
  import { __ensurePred } from "./internal/ensure.js";
5
+ import { maskedPath } from "./mask.js";
5
6
  const files = (dir, match = "", maxDepth = Infinity, logger) => __files(dir, match, logger, maxDepth, 0);
6
7
  function* __files(dir, match = "", logger, maxDepth = Infinity, depth = 0) {
7
8
  if (depth >= maxDepth) return;
@@ -15,7 +16,7 @@ function* __files(dir, match = "", logger, maxDepth = Infinity, depth = 0) {
15
16
  yield curr;
16
17
  }
17
18
  } catch (e) {
18
- logger && logger.warn(`ignoring file: ${f} (${e.message})`);
19
+ __error(logger, f, e);
19
20
  }
20
21
  }
21
22
  }
@@ -31,10 +32,13 @@ function* __dirs(dir, match = "", logger, maxDepth = Infinity, depth = 0) {
31
32
  yield* __dirs(curr, match, logger, maxDepth, depth + 1);
32
33
  }
33
34
  } catch (e) {
34
- logger && logger.warn(`ignoring file/dir: ${f} (${e.message})`);
35
+ __error(logger, f, e);
35
36
  }
36
37
  }
37
38
  }
39
+ const __error = (logger, path, e) => logger?.warn(
40
+ `ignoring: ${maskedPath(path)} (${maskedPath(e.message)})`
41
+ );
38
42
  export {
39
43
  dirs,
40
44
  files
package/hash.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { createReadStream } from "node:fs";
3
+ import { maskedPath } from "./mask.js";
3
4
  const fileHash = async (path, logger, algo = "sha256") => {
4
- logger?.info("reading file:", path);
5
+ logger?.info("reading file:", maskedPath(path));
5
6
  return await streamHash(createReadStream(path), logger, algo);
6
7
  };
7
8
  const streamHash = async (src, logger, algo = "sha256") => {
package/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./copy.js";
1
2
  export * from "./delete.js";
2
3
  export * from "./dir.js";
3
4
  export * from "./ext.js";
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./copy.js";
1
2
  export * from "./delete.js";
2
3
  export * from "./dir.js";
3
4
  export * from "./ext.js";
package/json.d.ts CHANGED
@@ -1,6 +1,19 @@
1
1
  import type { Fn3, Maybe, Nullable, NumOrString } from "@thi.ng/api";
2
2
  import type { ILogger } from "@thi.ng/logger";
3
+ /**
4
+ * Reads given file as UTF-8 and parses result as JSON of type `T`.
5
+ *
6
+ * @param path
7
+ * @param logger
8
+ */
3
9
  export declare const readJSON: <T = any>(path: string, logger?: ILogger) => T;
10
+ /**
11
+ * Async version of {@link readJSON}.
12
+ *
13
+ * @param path
14
+ * @param logger
15
+ */
16
+ export declare const readJSONAsync: <T = any>(path: string, logger?: ILogger) => Promise<T>;
4
17
  /**
5
18
  * Serializes `obj` to JSON and writes result to UTF-8 file `path`. See
6
19
  * {@link writeText} for more details.
@@ -17,4 +30,15 @@ export declare const readJSON: <T = any>(path: string, logger?: ILogger) => T;
17
30
  * @param dryRun
18
31
  */
19
32
  export declare const writeJSON: (path: string, obj: any, replacer?: Fn3<any, string, any, any> | Nullable<NumOrString[]>, space?: Maybe<NumOrString>, logger?: ILogger, dryRun?: boolean) => void;
33
+ /**
34
+ * Async version of {@link writeJSON}.
35
+ *
36
+ * @param path
37
+ * @param obj
38
+ * @param replacer
39
+ * @param space
40
+ * @param logger
41
+ * @param dryRun
42
+ */
43
+ export declare const writeJSONAsync: (path: string, obj: any, replacer?: Fn3<any, string, any, any> | Nullable<NumOrString[]>, space?: Maybe<NumOrString>, logger?: ILogger, dryRun?: boolean) => Promise<void>;
20
44
  //# sourceMappingURL=json.d.ts.map
package/json.js CHANGED
@@ -1,12 +1,21 @@
1
- import { readText, writeText } from "./text.js";
1
+ import { readText, readTextAsync, writeText, writeTextAsync } from "./text.js";
2
2
  const readJSON = (path, logger) => JSON.parse(readText(path, logger));
3
+ const readJSONAsync = async (path, logger) => JSON.parse(await readTextAsync(path, logger));
3
4
  const writeJSON = (path, obj, replacer, space, logger, dryRun = false) => writeText(
4
5
  path,
5
6
  JSON.stringify(obj, replacer, space) + "\n",
6
7
  logger,
7
8
  dryRun
8
9
  );
10
+ const writeJSONAsync = (path, obj, replacer, space, logger, dryRun = false) => writeTextAsync(
11
+ path,
12
+ JSON.stringify(obj, replacer, space) + "\n",
13
+ logger,
14
+ dryRun
15
+ );
9
16
  export {
10
17
  readJSON,
11
- writeJSON
18
+ readJSONAsync,
19
+ writeJSON,
20
+ writeJSONAsync
12
21
  };
package/mask.d.ts CHANGED
@@ -1,9 +1,51 @@
1
+ /**
2
+ * Adds given path pattern and its replacement string to registered path masks.
3
+ *
4
+ * @remarks
5
+ * By default only the `$HOME` dir will be masked (if that env var is defined).
6
+ *
7
+ * Also see {@link maskedPath} and {@link setPathMasks}.
8
+ *
9
+ * @param pattern
10
+ * @param mask
11
+ */
12
+ export declare const addPathMask: (pattern: string | RegExp, mask?: string) => void;
13
+ /**
14
+ * Overrides ALL registered path masks with those given.
15
+ *
16
+ * @remarks
17
+ * Also see {@link maskedPath} and {@link addPathMasks}.
18
+ *
19
+ * @param masks
20
+ */
21
+ export declare const setPathMasks: (masks: [RegExp, string][]) => [RegExp, string][];
22
+ /**
23
+ * Iteratively applies all registered path masks to given `path` and returns
24
+ * result.
25
+ *
26
+ * @remarks
27
+ * This function is used by all logging functions in this package. By default
28
+ * only the `$HOME` dir will be masked (if that env var is defined).
29
+ *
30
+ * See {@link addPathMask}, {@link setPathMasks}.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * maskedPath("/Users/asterix/foo/bar.txt");
35
+ * // "~/foo/bar.txt"
36
+ * ```
37
+ *
38
+ * @param path
39
+ */
40
+ export declare const maskedPath: (path: string) => string;
1
41
  /**
2
42
  * Replaces `home` (default: `process.env.HOME`) sub-path with given `mask`
3
- * (default: `~`).
43
+ * (default: `~`). Used by all path logging calls in this package.
4
44
  *
5
45
  * @param path
6
46
  * @param home
47
+ *
48
+ * @deprecated use {@link maskedPath} instead
7
49
  */
8
50
  export declare const maskHomeDir: (path: string, home?: string | undefined, mask?: string) => string;
9
51
  //# sourceMappingURL=mask.d.ts.map
package/mask.js CHANGED
@@ -1,4 +1,21 @@
1
+ import { isString } from "@thi.ng/checks/is-string";
2
+ let MASKS = [
3
+ [process.env.HOME ? new RegExp(process.env.HOME, "g") : /~/, "~"]
4
+ ];
5
+ const addPathMask = (pattern, mask = "****") => {
6
+ MASKS.push([isString(pattern) ? new RegExp(pattern, "g") : pattern, mask]);
7
+ };
8
+ const setPathMasks = (masks) => MASKS = masks;
9
+ const maskedPath = (path) => {
10
+ for (let [re, mask] of MASKS) {
11
+ path = path.replace(re, mask);
12
+ }
13
+ return path;
14
+ };
1
15
  const maskHomeDir = (path, home = process.env.HOME, mask = "~") => home ? path.replace(home, mask) : path;
2
16
  export {
3
- maskHomeDir
17
+ addPathMask,
18
+ maskHomeDir,
19
+ maskedPath,
20
+ setPathMasks
4
21
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@thi.ng/file-io",
3
- "version": "2.1.37",
4
- "description": "Assorted file I/O utils (with logging support) for NodeJS/Bun",
3
+ "version": "2.2.0",
4
+ "description": "Assorted file I/O utils (w/ logging support) for NodeJS/Bun",
5
5
  "type": "module",
6
6
  "module": "./index.js",
7
7
  "typings": "./index.d.ts",
@@ -39,11 +39,11 @@
39
39
  "tool:tangle": "../../node_modules/.bin/tangle src/**/*.ts"
40
40
  },
41
41
  "dependencies": {
42
- "@thi.ng/api": "^8.11.28",
43
- "@thi.ng/checks": "^3.7.8",
44
- "@thi.ng/hex": "^2.3.72",
45
- "@thi.ng/logger": "^3.1.9",
46
- "@thi.ng/random": "^4.1.19"
42
+ "@thi.ng/api": "^8.11.29",
43
+ "@thi.ng/checks": "^3.7.9",
44
+ "@thi.ng/hex": "^2.3.73",
45
+ "@thi.ng/logger": "^3.1.10",
46
+ "@thi.ng/random": "^4.1.20"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@types/node": "^22.15.21",
@@ -53,14 +53,19 @@
53
53
  },
54
54
  "keywords": [
55
55
  "async",
56
+ "delete",
57
+ "directory",
56
58
  "file",
57
59
  "hash",
60
+ "iterator",
58
61
  "json",
59
62
  "logger",
60
63
  "no-browser",
61
64
  "nodejs",
62
65
  "stream",
63
- "typescript"
66
+ "text",
67
+ "typescript",
68
+ "watch"
64
69
  ],
65
70
  "publishConfig": {
66
71
  "access": "public"
@@ -81,6 +86,9 @@
81
86
  ".": {
82
87
  "default": "./index.js"
83
88
  },
89
+ "./copy": {
90
+ "default": "./copy.js"
91
+ },
84
92
  "./delete": {
85
93
  "default": "./delete.js"
86
94
  },
@@ -126,5 +134,5 @@
126
134
  "status": "stable",
127
135
  "year": 2022
128
136
  },
129
- "gitHead": "61c3833b7ef7d044621454b5ea4af885d39f065e\n"
137
+ "gitHead": "06e582f962a9cd3abb905e91d97d652e1d3bb971\n"
130
138
  }
package/read.d.ts CHANGED
@@ -6,4 +6,11 @@ import type { ILogger } from "@thi.ng/logger";
6
6
  * @param logger
7
7
  */
8
8
  export declare const readBinary: (path: string, logger?: ILogger) => Uint8Array<ArrayBuffer>;
9
+ /**
10
+ * Async version of {@link readBinary}.
11
+ *
12
+ * @param path
13
+ * @param logger
14
+ */
15
+ export declare const readBinaryAsync: (path: string, logger?: ILogger) => Promise<Uint8Array<ArrayBufferLike>>;
9
16
  //# sourceMappingURL=read.d.ts.map
package/read.js CHANGED
@@ -1,9 +1,17 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { readFile } from "node:fs/promises";
3
+ import { maskedPath } from "./mask.js";
2
4
  const readBinary = (path, logger) => {
3
- logger?.debug("reading file:", path);
5
+ logger?.debug("reading file:", maskedPath(path));
4
6
  const buf = readFileSync(path);
5
7
  return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
6
8
  };
9
+ const readBinaryAsync = async (path, logger) => {
10
+ logger?.debug("reading file:", maskedPath(path));
11
+ const buf = await readFile(path);
12
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
13
+ };
7
14
  export {
8
- readBinary
15
+ readBinary,
16
+ readBinaryAsync
9
17
  };
package/temp.d.ts CHANGED
@@ -17,7 +17,7 @@ export declare const createTempFile: (body: string | TypedArray, logger?: ILogge
17
17
  *
18
18
  * @remarks
19
19
  * If no `name` is given, constructs a random filename of `tmp-XXX` (16 random
20
- * chars).
20
+ * chars). Default extension is an empty string.
21
21
  *
22
22
  * @param name
23
23
  * @param ext
package/temp.js CHANGED
@@ -4,14 +4,15 @@ import { realpathSync, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { sep } from "node:path";
6
6
  import { ensureDirForFile } from "./dir.js";
7
+ import { maskedPath } from "./mask.js";
7
8
  const createTempFile = (body, logger, name, ext) => {
8
9
  const path = tempFilePath(name, ext);
9
- logger?.debug("creating temp file:", path);
10
+ logger?.info("creating temp file:", maskedPath(path));
10
11
  ensureDirForFile(path);
11
12
  writeFileSync(path, body, isString(body) ? "utf-8" : void 0);
12
13
  return path;
13
14
  };
14
- const tempFilePath = (name, ext = "") => realpathSync(tmpdir()) + sep + (name || randomID(16, "tmp-")) + ext;
15
+ const tempFilePath = (name = randomID(16, "tmp-"), ext = "") => realpathSync(tmpdir()) + sep + name + ext;
15
16
  export {
16
17
  createTempFile,
17
18
  tempFilePath
package/text.d.ts CHANGED
@@ -8,6 +8,14 @@ import type { ILogger } from "@thi.ng/logger";
8
8
  * @param encoding
9
9
  */
10
10
  export declare const readText: (path: string, logger?: ILogger, encoding?: Extract<BufferEncoding, "ascii" | "latin1" | "utf-8" | "utf-16le" | "ucs-2">) => string;
11
+ /**
12
+ * Async version of {@link readText}.
13
+ *
14
+ * @param path
15
+ * @param logger
16
+ * @param encoding
17
+ */
18
+ export declare const readTextAsync: (path: string, logger?: ILogger, encoding?: Extract<BufferEncoding, "ascii" | "latin1" | "utf-8" | "utf-16le" | "ucs-2">) => Promise<string>;
11
19
  /**
12
20
  * Writes `body` as UTF-8 file to given `path`. If `dryRun` is true (default:
13
21
  * false), the file WON'T be written, however if a `logger` is provided then at
@@ -19,4 +27,13 @@ export declare const readText: (path: string, logger?: ILogger, encoding?: Extra
19
27
  * @param dryRun
20
28
  */
21
29
  export declare const writeText: (path: string, body: string | string[], logger?: ILogger, dryRun?: boolean) => void;
30
+ /**
31
+ * Async version of {@link writeText}.
32
+ *
33
+ * @param path
34
+ * @param body
35
+ * @param logger
36
+ * @param dryRun
37
+ */
38
+ export declare const writeTextAsync: (path: string, body: string | string[], logger?: ILogger, dryRun?: boolean) => Promise<void>;
22
39
  //# sourceMappingURL=text.d.ts.map
package/text.js CHANGED
@@ -1,10 +1,16 @@
1
1
  import { isArray } from "@thi.ng/checks/is-array";
2
2
  import { readFileSync } from "node:fs";
3
- import { writeFile } from "./write.js";
3
+ import { readFile } from "node:fs/promises";
4
+ import { maskedPath } from "./mask.js";
5
+ import { writeFile, writeFileAsync } from "./write.js";
4
6
  const readText = (path, logger, encoding = "utf-8") => {
5
- logger?.debug("reading file:", path);
7
+ logger?.debug("reading file:", maskedPath(path));
6
8
  return readFileSync(path, encoding);
7
9
  };
10
+ const readTextAsync = (path, logger, encoding = "utf-8") => {
11
+ logger?.debug("reading file:", maskedPath(path));
12
+ return readFile(path, encoding);
13
+ };
8
14
  const writeText = (path, body, logger, dryRun = false) => writeFile(
9
15
  path,
10
16
  isArray(body) ? body.join("\n") : body,
@@ -12,7 +18,16 @@ const writeText = (path, body, logger, dryRun = false) => writeFile(
12
18
  logger,
13
19
  dryRun
14
20
  );
21
+ const writeTextAsync = (path, body, logger, dryRun = false) => writeFileAsync(
22
+ path,
23
+ isArray(body) ? body.join("\n") : body,
24
+ "utf-8",
25
+ logger,
26
+ dryRun
27
+ );
15
28
  export {
16
29
  readText,
17
- writeText
30
+ readTextAsync,
31
+ writeText,
32
+ writeTextAsync
18
33
  };
package/watch.d.ts CHANGED
@@ -44,5 +44,10 @@ export declare class Watcher implements IClear, INotify {
44
44
  removeListener(id: string, fn: Listener<string>, scope?: any): boolean;
45
45
  notify(event: Event<string>): boolean;
46
46
  }
47
+ /**
48
+ * Creates a new {@link Watcher} instance with given options.
49
+ *
50
+ * @param opts
51
+ */
47
52
  export declare const fileWatcher: (opts?: Partial<WatcherOpts>) => Watcher;
48
53
  //# sourceMappingURL=watch.d.ts.map
package/watch.js CHANGED
@@ -13,10 +13,11 @@ import {
13
13
  } from "@thi.ng/api";
14
14
  import { isString } from "@thi.ng/checks/is-string";
15
15
  import { NULL_LOGGER } from "@thi.ng/logger/null";
16
- import { watch as $watch, existsSync } from "node:fs";
16
+ import { existsSync, watch } from "node:fs";
17
17
  import { join } from "node:path";
18
18
  import { isDirectory } from "./dir.js";
19
19
  import { __ensurePred } from "./internal/ensure.js";
20
+ import { maskedPath } from "./mask.js";
20
21
  const EVENT_ADDED = "added";
21
22
  const EVENT_CHANGED = "changed";
22
23
  const EVENT_REMOVED = "removed";
@@ -40,8 +41,8 @@ let Watcher = class {
40
41
  if (this.watchers[path]) return false;
41
42
  const isDir = isDirectory(path);
42
43
  const pred = __ensurePred(opts?.ext || "");
43
- this.opts.logger.debug(`adding watcher for: ${path}`);
44
- this.watchers[path] = $watch(
44
+ this.opts.logger.debug(`adding watcher for:`, maskedPath(path));
45
+ this.watchers[path] = watch(
45
46
  path,
46
47
  { recursive: opts?.recursive !== false },
47
48
  (event, currPath) => {
@@ -49,19 +50,20 @@ let Watcher = class {
49
50
  currPath = isDir ? join(path, currPath) : path;
50
51
  if (!pred(currPath)) return;
51
52
  setTimeout(() => {
53
+ const mpath = maskedPath(currPath);
52
54
  if (event === "change") {
53
- this.opts.logger.info(`file changed: ${currPath}`);
55
+ this.opts.logger.info(`file changed: ${mpath}`);
54
56
  this.notify({
55
57
  id: EVENT_CHANGED,
56
58
  value: currPath
57
59
  });
58
60
  } else if (!isDir || path === currPath) {
59
- this.opts.logger.info(`file removed: ${path}`);
61
+ this.opts.logger.info(`file removed: ${mpath}`);
60
62
  this.notify({ id: EVENT_REMOVED, value: path });
61
63
  this.remove(path);
62
64
  } else {
63
65
  const id = existsSync(currPath) ? EVENT_ADDED : EVENT_REMOVED;
64
- this.opts.logger.info(`file ${id}: ${path}`);
66
+ this.opts.logger.info(`file ${id}: ${mpath}`);
65
67
  this.notify({ id, value: currPath });
66
68
  }
67
69
  }, this.opts.delay);
@@ -72,7 +74,7 @@ let Watcher = class {
72
74
  remove(path) {
73
75
  const watcher = this.watchers[path];
74
76
  if (!watcher) return false;
75
- this.opts.logger.debug(`removing watcher for: ${path}`);
77
+ this.opts.logger.debug(`removing watcher for:`, maskedPath(path));
76
78
  watcher.close();
77
79
  delete this.watchers[path];
78
80
  return true;
package/write.d.ts CHANGED
@@ -13,4 +13,14 @@ import { type WriteFileOptions } from "node:fs";
13
13
  * @param dryRun
14
14
  */
15
15
  export declare const writeFile: (path: string, body: string | TypedArray, opts?: WriteFileOptions, logger?: ILogger, dryRun?: boolean) => void;
16
+ /**
17
+ * Async version of {@link writeFile}.
18
+ *
19
+ * @param path
20
+ * @param body
21
+ * @param opts
22
+ * @param logger
23
+ * @param dryRun
24
+ */
25
+ export declare const writeFileAsync: (path: string, body: string | TypedArray, opts?: WriteFileOptions, logger?: ILogger, dryRun?: boolean) => Promise<void>;
16
26
  //# sourceMappingURL=write.d.ts.map
package/write.js CHANGED
@@ -1,12 +1,19 @@
1
1
  import { isString } from "@thi.ng/checks/is-string";
2
2
  import { writeFileSync } from "node:fs";
3
+ import { writeFile as writeAsync } from "node:fs/promises";
3
4
  import { ensureDirForFile } from "./dir.js";
4
- const writeFile = (path, body, opts, logger, dryRun = false) => {
5
- logger?.info(`${dryRun ? "[dryrun] " : ""}writing file: ${path}`);
5
+ import { maskedPath } from "./mask.js";
6
+ const writeFile = (path, body, opts, logger, dryRun = false) => __write(writeFileSync, path, body, opts, logger, dryRun);
7
+ const writeFileAsync = (path, body, opts, logger, dryRun = false) => __write(writeAsync, path, body, opts, logger, dryRun);
8
+ const __write = (writeFn, path, body, opts, logger, dryRun = false) => {
9
+ logger?.info(
10
+ `${dryRun ? "[dryrun] " : ""}writing file: ${maskedPath(path)}`
11
+ );
6
12
  if (dryRun) return;
7
13
  ensureDirForFile(path);
8
- writeFileSync(path, body, !opts && isString(body) ? "utf-8" : opts);
14
+ return writeFn(path, body, !opts && isString(body) ? "utf-8" : opts);
9
15
  };
10
16
  export {
11
- writeFile
17
+ writeFile,
18
+ writeFileAsync
12
19
  };