atomically 1.7.0 → 2.0.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 (44) hide show
  1. package/.editorconfig +0 -3
  2. package/README.md +3 -3
  3. package/dist/{consts.d.ts → constants.d.ts} +6 -4
  4. package/dist/constants.js +19 -0
  5. package/dist/index.d.ts +6 -5
  6. package/dist/index.js +124 -105
  7. package/dist/types.d.ts +7 -7
  8. package/dist/types.js +2 -3
  9. package/dist/utils/lang.d.ts +6 -6
  10. package/dist/utils/lang.js +14 -14
  11. package/dist/utils/scheduler.d.ts +1 -1
  12. package/dist/utils/scheduler.js +4 -5
  13. package/dist/utils/temp.d.ts +1 -1
  14. package/dist/utils/temp.js +18 -15
  15. package/{LICENSE → license} +0 -0
  16. package/package.json +22 -32
  17. package/src/{consts.ts → constants.ts} +12 -4
  18. package/src/index.ts +153 -93
  19. package/src/types.ts +9 -9
  20. package/src/utils/lang.ts +18 -12
  21. package/src/utils/scheduler.ts +5 -3
  22. package/src/utils/temp.ts +18 -13
  23. package/tasks/benchmark.js +16 -12
  24. package/test/{basic.js → basic.cjs} +47 -49
  25. package/test/{concurrency.js → concurrency.cjs} +6 -8
  26. package/test/{integration.js → integration.cjs} +44 -46
  27. package/tsconfig.json +1 -26
  28. package/.nvmrc +0 -1
  29. package/dist/consts.js +0 -28
  30. package/dist/utils/attemptify.d.ts +0 -4
  31. package/dist/utils/attemptify.js +0 -25
  32. package/dist/utils/fs.d.ts +0 -34
  33. package/dist/utils/fs.js +0 -42
  34. package/dist/utils/fs_handlers.d.ts +0 -7
  35. package/dist/utils/fs_handlers.js +0 -28
  36. package/dist/utils/retryify.d.ts +0 -4
  37. package/dist/utils/retryify.js +0 -45
  38. package/dist/utils/retryify_queue.d.ts +0 -15
  39. package/dist/utils/retryify_queue.js +0 -58
  40. package/src/utils/attemptify.ts +0 -42
  41. package/src/utils/fs.ts +0 -51
  42. package/src/utils/fs_handlers.ts +0 -45
  43. package/src/utils/retryify.ts +0 -78
  44. package/src/utils/retryify_queue.ts +0 -95
package/.editorconfig CHANGED
@@ -8,6 +8,3 @@ indent_size = 2
8
8
  indent_style = space
9
9
  insert_final_newline = true
10
10
  trim_trailing_whitespace = true
11
-
12
- [*.md]
13
- trim_trailing_whitespace = false
package/README.md CHANGED
@@ -8,7 +8,7 @@ Read and write files atomically and reliably.
8
8
  - This library is a rewrite of [`write-file-atomic`](https://github.com/npm/write-file-atomic), with some important enhancements on top, you can largely use this as a drop-in replacement.
9
9
  - This library is written in TypeScript, so types aren't an afterthought but come with library.
10
10
  - This library is slightly faster than [`write-file-atomic`](https://github.com/npm/write-file-atomic), and it can be 10x faster, while being essentially just as safe, by using the `fsyncWait` option.
11
- - This library has 0 dependencies, so there's less code to vet and the entire thing is roughly 20% smaller than [`write-file-atomic`](https://github.com/npm/write-file-atomic).
11
+ - This library has 0 third-party dependencies, so there's less code to vet and the entire thing is roughly 20% smaller than [`write-file-atomic`](https://github.com/npm/write-file-atomic).
12
12
  - This library tries harder to write files on disk than [`write-file-atomic`](https://github.com/npm/write-file-atomic) does, by default retrying some failed operations and handling some more errors.
13
13
  - Reliability:
14
14
  - Reads are retried, when appropriate, until they succeed or the timeout is reached.
@@ -49,8 +49,8 @@ Read and write files atomically and reliably.
49
49
  - `schedule`: it's a function that returns a promise that resolves to a disposer function, basically it allows you to provide some custom queueing logic for the writing operation, allowing you to perhaps wire `atomically` with your app's main filesystem job scheduler:
50
50
  - even when a custom `schedule` function is provided write operations will still be queued internally by the library too.
51
51
  - `timeout`: it allows you to specify the amount of maximum milliseconds within which the library will retry some failed operations:
52
- - when writing asynchronously by default it will keep retrying for 5000 milliseconds.
53
- - when writing synchronously by default it will keep retrying for 100 milliseconds.
52
+ - when writing asynchronously by default it will keep retrying for 7500 milliseconds.
53
+ - when writing synchronously by default it will keep retrying for 1000 milliseconds.
54
54
  - if `0` or `-1` no failed operations will be retried.
55
55
  - if another number is provided that will be the timeout interval.
56
56
  - `tmpCreate`: it's a function that will be used to create the custom temporary file path in place of the default one:
@@ -3,11 +3,13 @@ declare const DEFAULT_FILE_MODE = 438;
3
3
  declare const DEFAULT_FOLDER_MODE = 511;
4
4
  declare const DEFAULT_READ_OPTIONS: {};
5
5
  declare const DEFAULT_WRITE_OPTIONS: {};
6
- declare const DEFAULT_TIMEOUT_ASYNC = 5000;
7
- declare const DEFAULT_TIMEOUT_SYNC = 100;
8
- declare const IS_POSIX = true;
6
+ declare const DEFAULT_USER_UID: number;
7
+ declare const DEFAULT_USER_GID: number;
8
+ declare const DEFAULT_TIMEOUT_ASYNC = 7500;
9
+ declare const DEFAULT_TIMEOUT_SYNC = 1000;
10
+ declare const IS_POSIX: boolean;
9
11
  declare const IS_USER_ROOT: boolean;
10
12
  declare const LIMIT_BASENAME_LENGTH = 128;
11
13
  declare const LIMIT_FILES_DESCRIPTORS = 10000;
12
14
  declare const NOOP: () => void;
13
- export { DEFAULT_ENCODING, DEFAULT_FILE_MODE, DEFAULT_FOLDER_MODE, DEFAULT_READ_OPTIONS, DEFAULT_WRITE_OPTIONS, DEFAULT_TIMEOUT_ASYNC, DEFAULT_TIMEOUT_SYNC, IS_POSIX, IS_USER_ROOT, LIMIT_BASENAME_LENGTH, LIMIT_FILES_DESCRIPTORS, NOOP };
15
+ export { DEFAULT_ENCODING, DEFAULT_FILE_MODE, DEFAULT_FOLDER_MODE, DEFAULT_READ_OPTIONS, DEFAULT_WRITE_OPTIONS, DEFAULT_USER_UID, DEFAULT_USER_GID, DEFAULT_TIMEOUT_ASYNC, DEFAULT_TIMEOUT_SYNC, IS_POSIX, IS_USER_ROOT, LIMIT_BASENAME_LENGTH, LIMIT_FILES_DESCRIPTORS, NOOP };
@@ -0,0 +1,19 @@
1
+ /* IMPORT */
2
+ import os from 'node:os';
3
+ /* MAIN */
4
+ const DEFAULT_ENCODING = 'utf8';
5
+ const DEFAULT_FILE_MODE = 0o666;
6
+ const DEFAULT_FOLDER_MODE = 0o777;
7
+ const DEFAULT_READ_OPTIONS = {};
8
+ const DEFAULT_WRITE_OPTIONS = {};
9
+ const DEFAULT_USER_UID = os.userInfo().uid;
10
+ const DEFAULT_USER_GID = os.userInfo().gid;
11
+ const DEFAULT_TIMEOUT_ASYNC = 7500;
12
+ const DEFAULT_TIMEOUT_SYNC = 1000;
13
+ const IS_POSIX = !!process.getuid;
14
+ const IS_USER_ROOT = process.getuid ? !process.getuid() : false;
15
+ const LIMIT_BASENAME_LENGTH = 128; //TODO: fetch the real limit from the filesystem //TODO: fetch the whole-path length limit too
16
+ const LIMIT_FILES_DESCRIPTORS = 10000; //TODO: fetch the real limit from the filesystem
17
+ const NOOP = () => { };
18
+ /* EXPORT */
19
+ export { DEFAULT_ENCODING, DEFAULT_FILE_MODE, DEFAULT_FOLDER_MODE, DEFAULT_READ_OPTIONS, DEFAULT_WRITE_OPTIONS, DEFAULT_USER_UID, DEFAULT_USER_GID, DEFAULT_TIMEOUT_ASYNC, DEFAULT_TIMEOUT_SYNC, IS_POSIX, IS_USER_ROOT, LIMIT_BASENAME_LENGTH, LIMIT_FILES_DESCRIPTORS, NOOP };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,14 @@
1
1
  /// <reference types="node" />
2
- import { Callback, Data, Path, ReadOptions, WriteOptions } from './types';
3
- declare function readFile(filePath: Path, options: string | ReadOptions & {
2
+ import type { Callback, Data, Encoding, Path, ReadOptions, WriteOptions } from './types';
3
+ declare function readFile(filePath: Path, options: Encoding | ReadOptions & {
4
4
  encoding: string;
5
5
  }): Promise<string>;
6
6
  declare function readFile(filePath: Path, options?: ReadOptions): Promise<Buffer>;
7
- declare function readFileSync(filePath: Path, options: string | ReadOptions & {
7
+ declare function readFileSync(filePath: Path, options: Encoding | ReadOptions & {
8
8
  encoding: string;
9
9
  }): string;
10
10
  declare function readFileSync(filePath: Path, options?: ReadOptions): Buffer;
11
- declare const writeFile: (filePath: Path, data: Data, options?: string | WriteOptions | Callback | undefined, callback?: Callback | undefined) => Promise<void>;
12
- declare const writeFileSync: (filePath: Path, data: Data, options?: string | WriteOptions) => void;
11
+ declare function writeFile(filePath: Path, data: Data, callback?: Callback): Promise<void>;
12
+ declare function writeFile(filePath: Path, data: Data, options?: Encoding | WriteOptions, callback?: Callback): Promise<void>;
13
+ declare const writeFileSync: (filePath: Path, data: Data, options?: Encoding | WriteOptions) => void;
13
14
  export { readFile, readFileSync, writeFile, writeFileSync };
package/dist/index.js CHANGED
@@ -1,177 +1,196 @@
1
- "use strict";
2
1
  /* IMPORT */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.writeFileSync = exports.writeFile = exports.readFileSync = exports.readFile = void 0;
5
- const path = require("path");
6
- const consts_1 = require("./consts");
7
- const fs_1 = require("./utils/fs");
8
- const lang_1 = require("./utils/lang");
9
- const scheduler_1 = require("./utils/scheduler");
10
- const temp_1 = require("./utils/temp");
11
- function readFile(filePath, options = consts_1.DEFAULT_READ_OPTIONS) {
12
- var _a;
13
- if (lang_1.default.isString(options))
2
+ import path from 'node:path';
3
+ import fs from 'stubborn-fs';
4
+ import { DEFAULT_ENCODING, DEFAULT_FILE_MODE, DEFAULT_FOLDER_MODE, DEFAULT_READ_OPTIONS, DEFAULT_WRITE_OPTIONS, DEFAULT_USER_UID, DEFAULT_USER_GID, DEFAULT_TIMEOUT_ASYNC, DEFAULT_TIMEOUT_SYNC, IS_POSIX } from './constants.js';
5
+ import { isException, isFunction, isString, isUndefined } from './utils/lang.js';
6
+ import Scheduler from './utils/scheduler.js';
7
+ import Temp from './utils/temp.js';
8
+ function readFile(filePath, options = DEFAULT_READ_OPTIONS) {
9
+ if (isString(options))
14
10
  return readFile(filePath, { encoding: options });
15
- const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_ASYNC);
16
- return fs_1.default.readFileRetry(timeout)(filePath, options);
11
+ const timeout = Date.now() + ((options.timeout ?? DEFAULT_TIMEOUT_ASYNC) || -1);
12
+ return fs.retry.readFile(timeout)(filePath, options);
17
13
  }
18
- exports.readFile = readFile;
19
- ;
20
- function readFileSync(filePath, options = consts_1.DEFAULT_READ_OPTIONS) {
21
- var _a;
22
- if (lang_1.default.isString(options))
14
+ function readFileSync(filePath, options = DEFAULT_READ_OPTIONS) {
15
+ if (isString(options))
23
16
  return readFileSync(filePath, { encoding: options });
24
- const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_SYNC);
25
- return fs_1.default.readFileSyncRetry(timeout)(filePath, options);
17
+ const timeout = Date.now() + ((options.timeout ?? DEFAULT_TIMEOUT_SYNC) || -1);
18
+ return fs.retry.readFileSync(timeout)(filePath, options);
26
19
  }
27
- exports.readFileSync = readFileSync;
28
- ;
29
- const writeFile = (filePath, data, options, callback) => {
30
- if (lang_1.default.isFunction(options))
31
- return writeFile(filePath, data, consts_1.DEFAULT_WRITE_OPTIONS, options);
20
+ function writeFile(filePath, data, options, callback) {
21
+ if (isFunction(options))
22
+ return writeFile(filePath, data, DEFAULT_WRITE_OPTIONS, options);
32
23
  const promise = writeFileAsync(filePath, data, options);
33
24
  if (callback)
34
25
  promise.then(callback, callback);
35
26
  return promise;
36
- };
37
- exports.writeFile = writeFile;
38
- const writeFileAsync = async (filePath, data, options = consts_1.DEFAULT_WRITE_OPTIONS) => {
39
- var _a;
40
- if (lang_1.default.isString(options))
27
+ }
28
+ async function writeFileAsync(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
29
+ if (isString(options))
41
30
  return writeFileAsync(filePath, data, { encoding: options });
42
- const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_ASYNC);
43
- let schedulerCustomDisposer = null, schedulerDisposer = null, tempDisposer = null, tempPath = null, fd = null;
31
+ const timeout = Date.now() + ((options.timeout ?? DEFAULT_TIMEOUT_ASYNC) || -1);
32
+ let schedulerCustomDisposer = null;
33
+ let schedulerDisposer = null;
34
+ let tempDisposer = null;
35
+ let tempPath = null;
36
+ let fd = null;
44
37
  try {
45
38
  if (options.schedule)
46
39
  schedulerCustomDisposer = await options.schedule(filePath);
47
- schedulerDisposer = await scheduler_1.default.schedule(filePath);
48
- filePath = await fs_1.default.realpathAttempt(filePath) || filePath;
49
- [tempPath, tempDisposer] = temp_1.default.get(filePath, options.tmpCreate || temp_1.default.create, !(options.tmpPurge === false));
50
- const useStatChown = consts_1.IS_POSIX && lang_1.default.isUndefined(options.chown), useStatMode = lang_1.default.isUndefined(options.mode);
51
- if (useStatChown || useStatMode) {
52
- const stat = await fs_1.default.statAttempt(filePath);
53
- if (stat) {
40
+ schedulerDisposer = await Scheduler.schedule(filePath);
41
+ const filePathReal = await fs.attempt.realpath(filePath);
42
+ const filePathExists = !!filePathReal;
43
+ filePath = filePathReal || filePath;
44
+ [tempPath, tempDisposer] = Temp.get(filePath, options.tmpCreate || Temp.create, !(options.tmpPurge === false));
45
+ const useStatChown = IS_POSIX && isUndefined(options.chown);
46
+ const useStatMode = isUndefined(options.mode);
47
+ if (filePathExists && (useStatChown || useStatMode)) {
48
+ const stats = await fs.attempt.stat(filePath);
49
+ if (stats) {
54
50
  options = { ...options };
55
- if (useStatChown)
56
- options.chown = { uid: stat.uid, gid: stat.gid };
57
- if (useStatMode)
58
- options.mode = stat.mode;
51
+ if (useStatChown) {
52
+ options.chown = { uid: stats.uid, gid: stats.gid };
53
+ }
54
+ if (useStatMode) {
55
+ options.mode = stats.mode;
56
+ }
59
57
  }
60
58
  }
61
- const parentPath = path.dirname(filePath);
62
- await fs_1.default.mkdirAttempt(parentPath, {
63
- mode: consts_1.DEFAULT_FOLDER_MODE,
64
- recursive: true
65
- });
66
- fd = await fs_1.default.openRetry(timeout)(tempPath, 'w', options.mode || consts_1.DEFAULT_FILE_MODE);
67
- if (options.tmpCreated)
59
+ if (!filePathExists) {
60
+ const parentPath = path.dirname(filePath);
61
+ await fs.attempt.mkdir(parentPath, {
62
+ mode: DEFAULT_FOLDER_MODE,
63
+ recursive: true
64
+ });
65
+ }
66
+ fd = await fs.retry.open(timeout)(tempPath, 'w', options.mode || DEFAULT_FILE_MODE);
67
+ if (options.tmpCreated) {
68
68
  options.tmpCreated(tempPath);
69
- if (lang_1.default.isString(data)) {
70
- await fs_1.default.writeRetry(timeout)(fd, data, 0, options.encoding || consts_1.DEFAULT_ENCODING);
71
69
  }
72
- else if (!lang_1.default.isUndefined(data)) {
73
- await fs_1.default.writeRetry(timeout)(fd, data, 0, data.length, 0);
70
+ if (isString(data)) {
71
+ await fs.retry.write(timeout)(fd, data, 0, options.encoding || DEFAULT_ENCODING);
72
+ }
73
+ else if (!isUndefined(data)) {
74
+ await fs.retry.write(timeout)(fd, data, 0, data.length, 0);
74
75
  }
75
76
  if (options.fsync !== false) {
76
77
  if (options.fsyncWait !== false) {
77
- await fs_1.default.fsyncRetry(timeout)(fd);
78
+ await fs.retry.fsync(timeout)(fd);
78
79
  }
79
80
  else {
80
- fs_1.default.fsyncAttempt(fd);
81
+ fs.attempt.fsync(fd);
81
82
  }
82
83
  }
83
- await fs_1.default.closeRetry(timeout)(fd);
84
+ await fs.retry.close(timeout)(fd);
84
85
  fd = null;
85
- if (options.chown)
86
- await fs_1.default.chownAttempt(tempPath, options.chown.uid, options.chown.gid);
87
- if (options.mode)
88
- await fs_1.default.chmodAttempt(tempPath, options.mode);
86
+ if (options.chown && (options.chown.uid !== DEFAULT_USER_UID || options.chown.gid !== DEFAULT_USER_GID)) {
87
+ await fs.attempt.chown(tempPath, options.chown.uid, options.chown.gid);
88
+ }
89
+ if (options.mode && options.mode !== DEFAULT_FILE_MODE) {
90
+ await fs.attempt.chmod(tempPath, options.mode);
91
+ }
89
92
  try {
90
- await fs_1.default.renameRetry(timeout)(tempPath, filePath);
93
+ await fs.retry.rename(timeout)(tempPath, filePath);
91
94
  }
92
95
  catch (error) {
96
+ if (!isException(error))
97
+ throw error;
93
98
  if (error.code !== 'ENAMETOOLONG')
94
99
  throw error;
95
- await fs_1.default.renameRetry(timeout)(tempPath, temp_1.default.truncate(filePath));
100
+ await fs.retry.rename(timeout)(tempPath, Temp.truncate(filePath));
96
101
  }
97
102
  tempDisposer();
98
103
  tempPath = null;
99
104
  }
100
105
  finally {
101
106
  if (fd)
102
- await fs_1.default.closeAttempt(fd);
107
+ await fs.attempt.close(fd);
103
108
  if (tempPath)
104
- temp_1.default.purge(tempPath);
109
+ Temp.purge(tempPath);
105
110
  if (schedulerCustomDisposer)
106
111
  schedulerCustomDisposer();
107
112
  if (schedulerDisposer)
108
113
  schedulerDisposer();
109
114
  }
110
- };
111
- const writeFileSync = (filePath, data, options = consts_1.DEFAULT_WRITE_OPTIONS) => {
112
- var _a;
113
- if (lang_1.default.isString(options))
115
+ }
116
+ const writeFileSync = (filePath, data, options = DEFAULT_WRITE_OPTIONS) => {
117
+ if (isString(options))
114
118
  return writeFileSync(filePath, data, { encoding: options });
115
- const timeout = Date.now() + ((_a = options.timeout) !== null && _a !== void 0 ? _a : consts_1.DEFAULT_TIMEOUT_SYNC);
116
- let tempDisposer = null, tempPath = null, fd = null;
119
+ const timeout = Date.now() + ((options.timeout ?? DEFAULT_TIMEOUT_SYNC) || -1);
120
+ let tempDisposer = null;
121
+ let tempPath = null;
122
+ let fd = null;
117
123
  try {
118
- filePath = fs_1.default.realpathSyncAttempt(filePath) || filePath;
119
- [tempPath, tempDisposer] = temp_1.default.get(filePath, options.tmpCreate || temp_1.default.create, !(options.tmpPurge === false));
120
- const useStatChown = consts_1.IS_POSIX && lang_1.default.isUndefined(options.chown), useStatMode = lang_1.default.isUndefined(options.mode);
121
- if (useStatChown || useStatMode) {
122
- const stat = fs_1.default.statSyncAttempt(filePath);
123
- if (stat) {
124
+ const filePathReal = fs.attempt.realpathSync(filePath);
125
+ const filePathExists = !!filePathReal;
126
+ filePath = filePathReal || filePath;
127
+ [tempPath, tempDisposer] = Temp.get(filePath, options.tmpCreate || Temp.create, !(options.tmpPurge === false));
128
+ const useStatChown = IS_POSIX && isUndefined(options.chown);
129
+ const useStatMode = isUndefined(options.mode);
130
+ if (filePathExists && (useStatChown || useStatMode)) {
131
+ const stats = fs.attempt.statSync(filePath);
132
+ if (stats) {
124
133
  options = { ...options };
125
- if (useStatChown)
126
- options.chown = { uid: stat.uid, gid: stat.gid };
127
- if (useStatMode)
128
- options.mode = stat.mode;
134
+ if (useStatChown) {
135
+ options.chown = { uid: stats.uid, gid: stats.gid };
136
+ }
137
+ if (useStatMode) {
138
+ options.mode = stats.mode;
139
+ }
129
140
  }
130
141
  }
131
- const parentPath = path.dirname(filePath);
132
- fs_1.default.mkdirSyncAttempt(parentPath, {
133
- mode: consts_1.DEFAULT_FOLDER_MODE,
134
- recursive: true
135
- });
136
- fd = fs_1.default.openSyncRetry(timeout)(tempPath, 'w', options.mode || consts_1.DEFAULT_FILE_MODE);
137
- if (options.tmpCreated)
142
+ if (!filePathExists) {
143
+ const parentPath = path.dirname(filePath);
144
+ fs.attempt.mkdirSync(parentPath, {
145
+ mode: DEFAULT_FOLDER_MODE,
146
+ recursive: true
147
+ });
148
+ }
149
+ fd = fs.retry.openSync(timeout)(tempPath, 'w', options.mode || DEFAULT_FILE_MODE);
150
+ if (options.tmpCreated) {
138
151
  options.tmpCreated(tempPath);
139
- if (lang_1.default.isString(data)) {
140
- fs_1.default.writeSyncRetry(timeout)(fd, data, 0, options.encoding || consts_1.DEFAULT_ENCODING);
141
152
  }
142
- else if (!lang_1.default.isUndefined(data)) {
143
- fs_1.default.writeSyncRetry(timeout)(fd, data, 0, data.length, 0);
153
+ if (isString(data)) {
154
+ fs.retry.writeSync(timeout)(fd, data, 0, options.encoding || DEFAULT_ENCODING);
155
+ }
156
+ else if (!isUndefined(data)) {
157
+ fs.retry.writeSync(timeout)(fd, data, 0, data.length, 0);
144
158
  }
145
159
  if (options.fsync !== false) {
146
160
  if (options.fsyncWait !== false) {
147
- fs_1.default.fsyncSyncRetry(timeout)(fd);
161
+ fs.retry.fsyncSync(timeout)(fd);
148
162
  }
149
163
  else {
150
- fs_1.default.fsyncAttempt(fd);
164
+ fs.attempt.fsync(fd);
151
165
  }
152
166
  }
153
- fs_1.default.closeSyncRetry(timeout)(fd);
167
+ fs.retry.closeSync(timeout)(fd);
154
168
  fd = null;
155
- if (options.chown)
156
- fs_1.default.chownSyncAttempt(tempPath, options.chown.uid, options.chown.gid);
157
- if (options.mode)
158
- fs_1.default.chmodSyncAttempt(tempPath, options.mode);
169
+ if (options.chown && (options.chown.uid !== DEFAULT_USER_UID || options.chown.gid !== DEFAULT_USER_GID)) {
170
+ fs.attempt.chownSync(tempPath, options.chown.uid, options.chown.gid);
171
+ }
172
+ if (options.mode && options.mode !== DEFAULT_FILE_MODE) {
173
+ fs.attempt.chmodSync(tempPath, options.mode);
174
+ }
159
175
  try {
160
- fs_1.default.renameSyncRetry(timeout)(tempPath, filePath);
176
+ fs.retry.renameSync(timeout)(tempPath, filePath);
161
177
  }
162
178
  catch (error) {
179
+ if (!isException(error))
180
+ throw error;
163
181
  if (error.code !== 'ENAMETOOLONG')
164
182
  throw error;
165
- fs_1.default.renameSyncRetry(timeout)(tempPath, temp_1.default.truncate(filePath));
183
+ fs.retry.renameSync(timeout)(tempPath, Temp.truncate(filePath));
166
184
  }
167
185
  tempDisposer();
168
186
  tempPath = null;
169
187
  }
170
188
  finally {
171
189
  if (fd)
172
- fs_1.default.closeSyncAttempt(fd);
190
+ fs.attempt.closeSync(fd);
173
191
  if (tempPath)
174
- temp_1.default.purge(tempPath);
192
+ Temp.purge(tempPath);
175
193
  }
176
194
  };
177
- exports.writeFileSync = writeFileSync;
195
+ /* EXPORT */
196
+ export { readFile, readFileSync, writeFile, writeFileSync };
package/dist/types.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  /// <reference types="node" />
2
- declare type Callback = (error: Exception | void) => any;
3
- declare type Data = Buffer | string | undefined;
2
+ declare type Callback = (error: Exception | void) => void;
3
+ declare type Data = Uint8Array | string | undefined;
4
4
  declare type Disposer = () => void;
5
+ declare type Encoding = 'ascii' | 'base64' | 'binary' | 'hex' | 'latin1' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2';
5
6
  declare type Exception = NodeJS.ErrnoException;
6
- declare type FN<Arguments extends any[] = any[], Return = any> = (...args: Arguments) => Return;
7
7
  declare type Path = string;
8
8
  declare type ReadOptions = {
9
- encoding?: string | null;
9
+ encoding?: Encoding | null;
10
10
  mode?: string | number | false;
11
11
  timeout?: number;
12
12
  };
@@ -15,14 +15,14 @@ declare type WriteOptions = {
15
15
  gid: number;
16
16
  uid: number;
17
17
  } | false;
18
- encoding?: string | null;
18
+ encoding?: Encoding | null;
19
19
  fsync?: boolean;
20
20
  fsyncWait?: boolean;
21
21
  mode?: string | number | false;
22
22
  schedule?: (filePath: string) => Promise<Disposer>;
23
23
  timeout?: number;
24
24
  tmpCreate?: (filePath: string) => string;
25
- tmpCreated?: (filePath: string) => any;
25
+ tmpCreated?: (filePath: string) => void;
26
26
  tmpPurge?: boolean;
27
27
  };
28
- export { Callback, Data, Disposer, Exception, FN, Path, ReadOptions, WriteOptions };
28
+ export type { Callback, Data, Disposer, Encoding, Exception, Path, ReadOptions, WriteOptions };
package/dist/types.js CHANGED
@@ -1,3 +1,2 @@
1
- "use strict";
2
- /* TYPES */
3
- Object.defineProperty(exports, "__esModule", { value: true });
1
+ /* MAIN */
2
+ export {};
@@ -1,6 +1,6 @@
1
- declare const Lang: {
2
- isFunction: (x: any) => x is Function;
3
- isString: (x: any) => x is string;
4
- isUndefined: (x: any) => x is undefined;
5
- };
6
- export default Lang;
1
+ /// <reference types="node" />
2
+ declare const isException: (value: unknown) => value is NodeJS.ErrnoException;
3
+ declare const isFunction: (value: unknown) => value is Function;
4
+ declare const isString: (value: unknown) => value is string;
5
+ declare const isUndefined: (value: unknown) => value is undefined;
6
+ export { isException, isFunction, isString, isUndefined };
@@ -1,16 +1,16 @@
1
- "use strict";
2
- /* LANG */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- const Lang = {
5
- isFunction: (x) => {
6
- return typeof x === 'function';
7
- },
8
- isString: (x) => {
9
- return typeof x === 'string';
10
- },
11
- isUndefined: (x) => {
12
- return typeof x === 'undefined';
13
- }
1
+ /* IMPORT */
2
+ /* MAIN */
3
+ const isException = (value) => {
4
+ return (value instanceof Error) && ('code' in value);
5
+ };
6
+ const isFunction = (value) => {
7
+ return (typeof value === 'function');
8
+ };
9
+ const isString = (value) => {
10
+ return (typeof value === 'string');
11
+ };
12
+ const isUndefined = (value) => {
13
+ return (value === undefined);
14
14
  };
15
15
  /* EXPORT */
16
- exports.default = Lang;
16
+ export { isException, isFunction, isString, isUndefined };
@@ -1,4 +1,4 @@
1
- import { Disposer } from '../types';
1
+ import type { Disposer } from '../types';
2
2
  declare const Scheduler: {
3
3
  next: (id: string) => void;
4
4
  schedule: (id: string) => Promise<Disposer>;
@@ -1,11 +1,10 @@
1
- "use strict";
2
1
  /* IMPORT */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- /* VARIABLES */
2
+ /* HELPERS */
5
3
  const Queues = {};
6
- /* SCHEDULER */
4
+ /* MAIN */
7
5
  //TODO: Maybe publish this as a standalone package
8
6
  const Scheduler = {
7
+ /* API */
9
8
  next: (id) => {
10
9
  const queue = Queues[id];
11
10
  if (!queue)
@@ -32,4 +31,4 @@ const Scheduler = {
32
31
  }
33
32
  };
34
33
  /* EXPORT */
35
- exports.default = Scheduler;
34
+ export default Scheduler;
@@ -1,4 +1,4 @@
1
- import { Disposer } from '../types';
1
+ import type { Disposer } from '../types';
2
2
  declare const Temp: {
3
3
  store: Record<string, boolean>;
4
4
  create: (filePath: string) => string;
@@ -1,17 +1,20 @@
1
- "use strict";
2
1
  /* IMPORT */
3
- Object.defineProperty(exports, "__esModule", { value: true });
4
- const path = require("path");
5
- const consts_1 = require("../consts");
6
- const fs_1 = require("./fs");
7
- /* TEMP */
2
+ import path from 'node:path';
3
+ import fs from 'stubborn-fs';
4
+ import whenExit from 'when-exit';
5
+ import { LIMIT_BASENAME_LENGTH } from '../constants.js';
6
+ /* MAIN */
8
7
  //TODO: Maybe publish this as a standalone package
9
8
  const Temp = {
9
+ /* VARIABLES */
10
10
  store: {},
11
+ /* API */
11
12
  create: (filePath) => {
12
- const randomness = `000000${Math.floor(Math.random() * 16777215).toString(16)}`.slice(-6), // 6 random-enough hex characters
13
- timestamp = Date.now().toString().slice(-10), // 10 precise timestamp digits
14
- prefix = 'tmp-', suffix = `.${prefix}${timestamp}${randomness}`, tempPath = `${filePath}${suffix}`;
13
+ const randomness = `000000${Math.floor(Math.random() * 16777215).toString(16)}`.slice(-6); // 6 random-enough hex characters
14
+ const timestamp = Date.now().toString().slice(-10); // 10 precise timestamp digits
15
+ const prefix = 'tmp-';
16
+ const suffix = `.${prefix}${timestamp}${randomness}`;
17
+ const tempPath = `${filePath}${suffix}`;
15
18
  return tempPath;
16
19
  },
17
20
  get: (filePath, creator, purge = true) => {
@@ -26,13 +29,13 @@ const Temp = {
26
29
  if (!Temp.store[filePath])
27
30
  return;
28
31
  delete Temp.store[filePath];
29
- fs_1.default.unlinkAttempt(filePath);
32
+ fs.attempt.unlink(filePath);
30
33
  },
31
34
  purgeSync: (filePath) => {
32
35
  if (!Temp.store[filePath])
33
36
  return;
34
37
  delete Temp.store[filePath];
35
- fs_1.default.unlinkSyncAttempt(filePath);
38
+ fs.attempt.unlinkSync(filePath);
36
39
  },
37
40
  purgeSyncAll: () => {
38
41
  for (const filePath in Temp.store) {
@@ -41,16 +44,16 @@ const Temp = {
41
44
  },
42
45
  truncate: (filePath) => {
43
46
  const basename = path.basename(filePath);
44
- if (basename.length <= consts_1.LIMIT_BASENAME_LENGTH)
47
+ if (basename.length <= LIMIT_BASENAME_LENGTH)
45
48
  return filePath; //FIXME: Rough and quick attempt at detecting ok lengths
46
49
  const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec(basename);
47
50
  if (!truncable)
48
51
  return filePath; //FIXME: No truncable part detected, can't really do much without also changing the parent path, which is unsafe, hoping for the best here
49
- const truncationLength = basename.length - consts_1.LIMIT_BASENAME_LENGTH;
52
+ const truncationLength = basename.length - LIMIT_BASENAME_LENGTH;
50
53
  return `${filePath.slice(0, -basename.length)}${truncable[1]}${truncable[2].slice(0, -truncationLength)}${truncable[3]}`; //FIXME: The truncable part might be shorter than needed here
51
54
  }
52
55
  };
53
56
  /* INIT */
54
- process.on('exit', Temp.purgeSyncAll); // Ensuring purgeable temp files are purged on exit
57
+ whenExit(Temp.purgeSyncAll); // Ensuring purgeable temp files are purged on exit
55
58
  /* EXPORT */
56
- exports.default = Temp;
59
+ export default Temp;
File without changes