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/package.json CHANGED
@@ -1,30 +1,23 @@
1
1
  {
2
2
  "name": "atomically",
3
+ "repository": "github:fabiospampinato/atomically",
3
4
  "description": "Read and write files atomically and reliably.",
4
- "version": "1.7.0",
5
+ "version": "2.0.0",
6
+ "type": "module",
5
7
  "main": "dist/index.js",
6
- "types": "dist/index.d.ts",
8
+ "exports": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
7
10
  "scripts": {
8
- "benchmark": "node ./tasks/benchmark.js",
9
- "clean": "rimraf dist",
10
- "compile": "tsc --skipLibCheck && tstei",
11
- "compile:watch": "tsc --skipLibCheck --watch",
12
- "test": "tap --no-coverage-report",
13
- "test:watch": "tap --no-coverage-report --watch",
11
+ "benchmark": "tsex benchmark",
12
+ "benchmarkLwatch": "tsex benchmark --watch",
13
+ "clean": "tsex clean",
14
+ "compile": "tsex compile",
15
+ "compile:watch": "tsex compile --watch",
16
+ "test:init": "esbuild --bundle --target=es2020 --platform=node --format=cjs src/index.ts > test/atomically.cjs",
17
+ "test": "npm run test:init && tap --no-check-coverage --no-coverage-report",
18
+ "test:watch": "npm run test:init && tap --no-check-coverage --no-coverage-report --watch",
14
19
  "prepublishOnly": "npm run clean && npm run compile && npm run test"
15
20
  },
16
- "bugs": {
17
- "url": "https://github.com/fabiospampinato/atomically/issues"
18
- },
19
- "license": "MIT",
20
- "author": {
21
- "name": "Fabio Spampinato",
22
- "email": "spampinabio@gmail.com"
23
- },
24
- "repository": {
25
- "type": "git",
26
- "url": "https://github.com/fabiospampinato/atomically.git"
27
- },
28
21
  "keywords": [
29
22
  "atomic",
30
23
  "read",
@@ -32,20 +25,17 @@
32
25
  "file",
33
26
  "reliable"
34
27
  ],
35
- "engines": {
36
- "node": ">=10.12.0"
28
+ "dependencies": {
29
+ "stubborn-fs": "^1.2.1",
30
+ "when-exit": "^2.0.0"
37
31
  },
38
- "dependencies": {},
39
32
  "devDependencies": {
40
- "@types/node": "^12.7.2",
41
- "lodash": "^4.17.19",
42
- "mkdirp": "^1.0.4",
43
- "promise-resolve-timeout": "^1.2.1",
33
+ "@types/node": "^18.11.9",
34
+ "esbuild": "^0.15.13",
44
35
  "require-inject": "^1.4.4",
45
- "rimraf": "^3.0.2",
46
- "tap": "^14.10.7",
47
- "typescript": "^3.5.3",
48
- "typescript-transform-export-interop": "^1.0.2",
49
- "write-file-atomic": "^3.0.3"
36
+ "tap": "^16.3.0",
37
+ "tsex": "^1.1.2",
38
+ "typescript": "^4.8.4",
39
+ "write-file-atomic": "^5.0.0"
50
40
  }
51
41
  }
@@ -1,5 +1,9 @@
1
1
 
2
- /* CONSTS */
2
+ /* IMPORT */
3
+
4
+ import os from 'node:os';
5
+
6
+ /* MAIN */
3
7
 
4
8
  const DEFAULT_ENCODING = 'utf8';
5
9
 
@@ -11,9 +15,13 @@ const DEFAULT_READ_OPTIONS = {};
11
15
 
12
16
  const DEFAULT_WRITE_OPTIONS = {};
13
17
 
14
- const DEFAULT_TIMEOUT_ASYNC = 5000;
18
+ const DEFAULT_USER_UID = os.userInfo ().uid;
19
+
20
+ const DEFAULT_USER_GID = os.userInfo ().gid;
21
+
22
+ const DEFAULT_TIMEOUT_ASYNC = 7500;
15
23
 
16
- const DEFAULT_TIMEOUT_SYNC = 100;
24
+ const DEFAULT_TIMEOUT_SYNC = 1000;
17
25
 
18
26
  const IS_POSIX = !!process.getuid;
19
27
 
@@ -27,4 +35,4 @@ const NOOP = () => {};
27
35
 
28
36
  /* EXPORT */
29
37
 
30
- 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};
38
+ 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/src/index.ts CHANGED
@@ -1,43 +1,45 @@
1
1
 
2
2
  /* IMPORT */
3
3
 
4
- import * as path from 'path';
5
- import {DEFAULT_ENCODING, DEFAULT_FILE_MODE, DEFAULT_FOLDER_MODE, DEFAULT_READ_OPTIONS, DEFAULT_WRITE_OPTIONS, DEFAULT_TIMEOUT_ASYNC, DEFAULT_TIMEOUT_SYNC, IS_POSIX} from './consts';
6
- import FS from './utils/fs';
7
- import Lang from './utils/lang';
4
+ import path from 'node:path';
5
+ import fs from 'stubborn-fs';
6
+ 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';
7
+ import {isException, isFunction, isString, isUndefined} from './utils/lang';
8
8
  import Scheduler from './utils/scheduler';
9
9
  import Temp from './utils/temp';
10
- import {Callback, Data, Disposer, Path, ReadOptions, WriteOptions} from './types';
10
+ import type {Callback, Data, Disposer, Encoding, Path, ReadOptions, WriteOptions} from './types';
11
11
 
12
- /* ATOMICALLY */
12
+ /* MAIN */
13
13
 
14
- function readFile ( filePath: Path, options: string | ReadOptions & { encoding: string } ): Promise<string>;
14
+ function readFile ( filePath: Path, options: Encoding | ReadOptions & { encoding: string } ): Promise<string>;
15
15
  function readFile ( filePath: Path, options?: ReadOptions ): Promise<Buffer>;
16
- function readFile ( filePath: Path, options: string | ReadOptions = DEFAULT_READ_OPTIONS ): Promise<Buffer | string> {
16
+ function readFile ( filePath: Path, options: Encoding | ReadOptions = DEFAULT_READ_OPTIONS ): Promise<Buffer | string> {
17
17
 
18
- if ( Lang.isString ( options ) ) return readFile ( filePath, { encoding: options } );
18
+ if ( isString ( options ) ) return readFile ( filePath, { encoding: options } );
19
19
 
20
- const timeout = Date.now () + ( options.timeout ?? DEFAULT_TIMEOUT_ASYNC );
20
+ const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_ASYNC ) || -1 );
21
21
 
22
- return FS.readFileRetry ( timeout )( filePath, options );
22
+ return fs.retry.readFile ( timeout )( filePath, options );
23
23
 
24
- };
24
+ }
25
25
 
26
- function readFileSync ( filePath: Path, options: string | ReadOptions & { encoding: string } ): string;
26
+ function readFileSync ( filePath: Path, options: Encoding | ReadOptions & { encoding: string } ): string;
27
27
  function readFileSync ( filePath: Path, options?: ReadOptions ): Buffer;
28
- function readFileSync ( filePath: Path, options: string | ReadOptions = DEFAULT_READ_OPTIONS ): Buffer | string {
28
+ function readFileSync ( filePath: Path, options: Encoding | ReadOptions = DEFAULT_READ_OPTIONS ): Buffer | string {
29
29
 
30
- if ( Lang.isString ( options ) ) return readFileSync ( filePath, { encoding: options } );
30
+ if ( isString ( options ) ) return readFileSync ( filePath, { encoding: options } );
31
31
 
32
- const timeout = Date.now () + ( options.timeout ?? DEFAULT_TIMEOUT_SYNC );
32
+ const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_SYNC ) || -1 );
33
33
 
34
- return FS.readFileSyncRetry ( timeout )( filePath, options );
34
+ return fs.retry.readFileSync ( timeout )( filePath, options );
35
35
 
36
- };
36
+ }
37
37
 
38
- const writeFile = ( filePath: Path, data: Data, options?: string | WriteOptions | Callback, callback?: Callback ): Promise<void> => {
38
+ function writeFile ( filePath: Path, data: Data, callback?: Callback ): Promise<void>;
39
+ function writeFile ( filePath: Path, data: Data, options?: Encoding | WriteOptions, callback?: Callback ): Promise<void>;
40
+ function writeFile ( filePath: Path, data: Data, options?: Encoding | WriteOptions | Callback, callback?: Callback ): Promise<void> {
39
41
 
40
- if ( Lang.isFunction ( options ) ) return writeFile ( filePath, data, DEFAULT_WRITE_OPTIONS, options );
42
+ if ( isFunction ( options ) ) return writeFile ( filePath, data, DEFAULT_WRITE_OPTIONS, options );
41
43
 
42
44
  const promise = writeFileAsync ( filePath, data, options );
43
45
 
@@ -45,19 +47,19 @@ const writeFile = ( filePath: Path, data: Data, options?: string | WriteOptions
45
47
 
46
48
  return promise;
47
49
 
48
- };
50
+ }
49
51
 
50
- const writeFileAsync = async ( filePath: Path, data: Data, options: string | WriteOptions = DEFAULT_WRITE_OPTIONS ): Promise<void> => {
52
+ async function writeFileAsync ( filePath: Path, data: Data, options: Encoding | WriteOptions = DEFAULT_WRITE_OPTIONS ): Promise<void> {
51
53
 
52
- if ( Lang.isString ( options ) ) return writeFileAsync ( filePath, data, { encoding: options } );
54
+ if ( isString ( options ) ) return writeFileAsync ( filePath, data, { encoding: options } );
53
55
 
54
- const timeout = Date.now () + ( options.timeout ?? DEFAULT_TIMEOUT_ASYNC );
56
+ const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_ASYNC ) || -1 );
55
57
 
56
- let schedulerCustomDisposer: Disposer | null = null,
57
- schedulerDisposer: Disposer | null = null,
58
- tempDisposer: Disposer | null = null,
59
- tempPath: string | null = null,
60
- fd: number | null = null;
58
+ let schedulerCustomDisposer: Disposer | null = null;
59
+ let schedulerDisposer: Disposer | null = null;
60
+ let tempDisposer: Disposer | null = null;
61
+ let tempPath: string | null = null;
62
+ let fd: number | null = null;
61
63
 
62
64
  try {
63
65
 
@@ -65,47 +67,66 @@ const writeFileAsync = async ( filePath: Path, data: Data, options: string | Wri
65
67
 
66
68
  schedulerDisposer = await Scheduler.schedule ( filePath );
67
69
 
68
- filePath = await FS.realpathAttempt ( filePath ) || filePath;
70
+ const filePathReal = await fs.attempt.realpath ( filePath );
71
+ const filePathExists = !!filePathReal;
72
+
73
+ filePath = filePathReal || filePath;
69
74
 
70
75
  [tempPath, tempDisposer] = Temp.get ( filePath, options.tmpCreate || Temp.create, !( options.tmpPurge === false ) );
71
76
 
72
- const useStatChown = IS_POSIX && Lang.isUndefined ( options.chown ),
73
- useStatMode = Lang.isUndefined ( options.mode );
77
+ const useStatChown = IS_POSIX && isUndefined ( options.chown );
78
+ const useStatMode = isUndefined ( options.mode );
74
79
 
75
- if ( useStatChown || useStatMode ) {
80
+ if ( filePathExists && ( useStatChown || useStatMode ) ) {
76
81
 
77
- const stat = await FS.statAttempt ( filePath );
82
+ const stats = await fs.attempt.stat ( filePath );
78
83
 
79
- if ( stat ) {
84
+ if ( stats ) {
80
85
 
81
86
  options = { ...options };
82
87
 
83
- if ( useStatChown ) options.chown = { uid: stat.uid, gid: stat.gid };
88
+ if ( useStatChown ) {
89
+
90
+ options.chown = { uid: stats.uid, gid: stats.gid };
91
+
92
+ }
93
+
94
+ if ( useStatMode ) {
95
+
96
+ options.mode = stats.mode;
84
97
 
85
- if ( useStatMode ) options.mode = stat.mode;
98
+ }
86
99
 
87
100
  }
88
101
 
89
102
  }
90
103
 
91
- const parentPath = path.dirname ( filePath );
104
+ if ( !filePathExists ) {
105
+
106
+ const parentPath = path.dirname ( filePath );
107
+
108
+ await fs.attempt.mkdir ( parentPath, {
109
+ mode: DEFAULT_FOLDER_MODE,
110
+ recursive: true
111
+ });
112
+
113
+ }
114
+
115
+ fd = await fs.retry.open ( timeout )( tempPath, 'w', options.mode || DEFAULT_FILE_MODE );
92
116
 
93
- await FS.mkdirAttempt ( parentPath, {
94
- mode: DEFAULT_FOLDER_MODE,
95
- recursive: true
96
- });
117
+ if ( options.tmpCreated ) {
97
118
 
98
- fd = await FS.openRetry ( timeout )( tempPath, 'w', options.mode || DEFAULT_FILE_MODE );
119
+ options.tmpCreated ( tempPath );
99
120
 
100
- if ( options.tmpCreated ) options.tmpCreated ( tempPath );
121
+ }
101
122
 
102
- if ( Lang.isString ( data ) ) {
123
+ if ( isString ( data ) ) {
103
124
 
104
- await FS.writeRetry ( timeout )( fd, data, 0, options.encoding || DEFAULT_ENCODING );
125
+ await fs.retry.write ( timeout )( fd, data, 0, options.encoding || DEFAULT_ENCODING );
105
126
 
106
- } else if ( !Lang.isUndefined ( data ) ) {
127
+ } else if ( !isUndefined ( data ) ) {
107
128
 
108
- await FS.writeRetry ( timeout )( fd, data, 0, data.length, 0 );
129
+ await fs.retry.write ( timeout )( fd, data, 0, data.length, 0 );
109
130
 
110
131
  }
111
132
 
@@ -113,33 +134,43 @@ const writeFileAsync = async ( filePath: Path, data: Data, options: string | Wri
113
134
 
114
135
  if ( options.fsyncWait !== false ) {
115
136
 
116
- await FS.fsyncRetry ( timeout )( fd );
137
+ await fs.retry.fsync ( timeout )( fd );
117
138
 
118
139
  } else {
119
140
 
120
- FS.fsyncAttempt ( fd );
141
+ fs.attempt.fsync ( fd );
121
142
 
122
143
  }
123
144
 
124
145
  }
125
146
 
126
- await FS.closeRetry ( timeout )( fd );
147
+ await fs.retry.close ( timeout )( fd );
127
148
 
128
149
  fd = null;
129
150
 
130
- if ( options.chown ) await FS.chownAttempt ( tempPath, options.chown.uid, options.chown.gid );
151
+ if ( options.chown && ( options.chown.uid !== DEFAULT_USER_UID || options.chown.gid !== DEFAULT_USER_GID ) ) {
152
+
153
+ await fs.attempt.chown ( tempPath, options.chown.uid, options.chown.gid );
154
+
155
+ }
131
156
 
132
- if ( options.mode ) await FS.chmodAttempt ( tempPath, options.mode );
157
+ if ( options.mode && options.mode !== DEFAULT_FILE_MODE ) {
158
+
159
+ await fs.attempt.chmod ( tempPath, options.mode );
160
+
161
+ }
133
162
 
134
163
  try {
135
164
 
136
- await FS.renameRetry ( timeout )( tempPath, filePath );
165
+ await fs.retry.rename ( timeout )( tempPath, filePath );
166
+
167
+ } catch ( error: unknown ) {
137
168
 
138
- } catch ( error ) {
169
+ if ( !isException ( error ) ) throw error;
139
170
 
140
171
  if ( error.code !== 'ENAMETOOLONG' ) throw error;
141
172
 
142
- await FS.renameRetry ( timeout )( tempPath, Temp.truncate ( filePath ) );
173
+ await fs.retry.rename ( timeout )( tempPath, Temp.truncate ( filePath ) );
143
174
 
144
175
  }
145
176
 
@@ -149,7 +180,7 @@ const writeFileAsync = async ( filePath: Path, data: Data, options: string | Wri
149
180
 
150
181
  } finally {
151
182
 
152
- if ( fd ) await FS.closeAttempt ( fd );
183
+ if ( fd ) await fs.attempt.close ( fd );
153
184
 
154
185
  if ( tempPath ) Temp.purge ( tempPath );
155
186
 
@@ -159,61 +190,80 @@ const writeFileAsync = async ( filePath: Path, data: Data, options: string | Wri
159
190
 
160
191
  }
161
192
 
162
- };
193
+ }
163
194
 
164
- const writeFileSync = ( filePath: Path, data: Data, options: string | WriteOptions = DEFAULT_WRITE_OPTIONS ): void => {
195
+ const writeFileSync = ( filePath: Path, data: Data, options: Encoding | WriteOptions = DEFAULT_WRITE_OPTIONS ): void => {
165
196
 
166
- if ( Lang.isString ( options ) ) return writeFileSync ( filePath, data, { encoding: options } );
197
+ if ( isString ( options ) ) return writeFileSync ( filePath, data, { encoding: options } );
167
198
 
168
- const timeout = Date.now () + ( options.timeout ?? DEFAULT_TIMEOUT_SYNC );
199
+ const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_SYNC ) || -1 );
169
200
 
170
- let tempDisposer: Disposer | null = null,
171
- tempPath: string | null = null,
172
- fd: number | null = null;
201
+ let tempDisposer: Disposer | null = null;
202
+ let tempPath: string | null = null;
203
+ let fd: number | null = null;
173
204
 
174
205
  try {
175
206
 
176
- filePath = FS.realpathSyncAttempt ( filePath ) || filePath;
207
+ const filePathReal = fs.attempt.realpathSync ( filePath );
208
+ const filePathExists = !!filePathReal;
209
+
210
+ filePath = filePathReal || filePath;
177
211
 
178
212
  [tempPath, tempDisposer] = Temp.get ( filePath, options.tmpCreate || Temp.create, !( options.tmpPurge === false ) );
179
213
 
180
- const useStatChown = IS_POSIX && Lang.isUndefined ( options.chown ),
181
- useStatMode = Lang.isUndefined ( options.mode );
214
+ const useStatChown = IS_POSIX && isUndefined ( options.chown );
215
+ const useStatMode = isUndefined ( options.mode );
182
216
 
183
- if ( useStatChown || useStatMode ) {
217
+ if ( filePathExists && ( useStatChown || useStatMode ) ) {
184
218
 
185
- const stat = FS.statSyncAttempt ( filePath );
219
+ const stats = fs.attempt.statSync ( filePath );
186
220
 
187
- if ( stat ) {
221
+ if ( stats ) {
188
222
 
189
223
  options = { ...options };
190
224
 
191
- if ( useStatChown ) options.chown = { uid: stat.uid, gid: stat.gid };
225
+ if ( useStatChown ) {
226
+
227
+ options.chown = { uid: stats.uid, gid: stats.gid };
228
+
229
+ }
230
+
231
+ if ( useStatMode ) {
232
+
233
+ options.mode = stats.mode;
192
234
 
193
- if ( useStatMode ) options.mode = stat.mode;
235
+ }
194
236
 
195
237
  }
196
238
 
197
239
  }
198
240
 
199
- const parentPath = path.dirname ( filePath );
241
+ if ( !filePathExists ) {
242
+
243
+ const parentPath = path.dirname ( filePath );
244
+
245
+ fs.attempt.mkdirSync ( parentPath, {
246
+ mode: DEFAULT_FOLDER_MODE,
247
+ recursive: true
248
+ });
249
+
250
+ }
251
+
252
+ fd = fs.retry.openSync ( timeout )( tempPath, 'w', options.mode || DEFAULT_FILE_MODE );
200
253
 
201
- FS.mkdirSyncAttempt ( parentPath, {
202
- mode: DEFAULT_FOLDER_MODE,
203
- recursive: true
204
- });
254
+ if ( options.tmpCreated ) {
205
255
 
206
- fd = FS.openSyncRetry ( timeout )( tempPath, 'w', options.mode || DEFAULT_FILE_MODE );
256
+ options.tmpCreated ( tempPath );
207
257
 
208
- if ( options.tmpCreated ) options.tmpCreated ( tempPath );
258
+ }
209
259
 
210
- if ( Lang.isString ( data ) ) {
260
+ if ( isString ( data ) ) {
211
261
 
212
- FS.writeSyncRetry ( timeout )( fd, data, 0, options.encoding || DEFAULT_ENCODING );
262
+ fs.retry.writeSync ( timeout )( fd, data, 0, options.encoding || DEFAULT_ENCODING );
213
263
 
214
- } else if ( !Lang.isUndefined ( data ) ) {
264
+ } else if ( !isUndefined ( data ) ) {
215
265
 
216
- FS.writeSyncRetry ( timeout )( fd, data, 0, data.length, 0 );
266
+ fs.retry.writeSync ( timeout )( fd, data, 0, data.length, 0 );
217
267
 
218
268
  }
219
269
 
@@ -221,33 +271,43 @@ const writeFileSync = ( filePath: Path, data: Data, options: string | WriteOptio
221
271
 
222
272
  if ( options.fsyncWait !== false ) {
223
273
 
224
- FS.fsyncSyncRetry ( timeout )( fd );
274
+ fs.retry.fsyncSync ( timeout )( fd );
225
275
 
226
276
  } else {
227
277
 
228
- FS.fsyncAttempt ( fd );
278
+ fs.attempt.fsync ( fd );
229
279
 
230
280
  }
231
281
 
232
282
  }
233
283
 
234
- FS.closeSyncRetry ( timeout )( fd );
284
+ fs.retry.closeSync ( timeout )( fd );
235
285
 
236
286
  fd = null;
237
287
 
238
- if ( options.chown ) FS.chownSyncAttempt ( tempPath, options.chown.uid, options.chown.gid );
288
+ if ( options.chown && ( options.chown.uid !== DEFAULT_USER_UID || options.chown.gid !== DEFAULT_USER_GID ) ) {
289
+
290
+ fs.attempt.chownSync ( tempPath, options.chown.uid, options.chown.gid );
291
+
292
+ }
239
293
 
240
- if ( options.mode ) FS.chmodSyncAttempt ( tempPath, options.mode );
294
+ if ( options.mode && options.mode !== DEFAULT_FILE_MODE ) {
295
+
296
+ fs.attempt.chmodSync ( tempPath, options.mode );
297
+
298
+ }
241
299
 
242
300
  try {
243
301
 
244
- FS.renameSyncRetry ( timeout )( tempPath, filePath );
302
+ fs.retry.renameSync ( timeout )( tempPath, filePath );
303
+
304
+ } catch ( error: unknown ) {
245
305
 
246
- } catch ( error ) {
306
+ if ( !isException ( error ) ) throw error;
247
307
 
248
308
  if ( error.code !== 'ENAMETOOLONG' ) throw error;
249
309
 
250
- FS.renameSyncRetry ( timeout )( tempPath, Temp.truncate ( filePath ) );
310
+ fs.retry.renameSync ( timeout )( tempPath, Temp.truncate ( filePath ) );
251
311
 
252
312
  }
253
313
 
@@ -257,13 +317,13 @@ const writeFileSync = ( filePath: Path, data: Data, options: string | WriteOptio
257
317
 
258
318
  } finally {
259
319
 
260
- if ( fd ) FS.closeSyncAttempt ( fd );
320
+ if ( fd ) fs.attempt.closeSync ( fd );
261
321
 
262
322
  if ( tempPath ) Temp.purge ( tempPath );
263
323
 
264
324
  }
265
325
 
266
- };
326
+ }
267
327
 
268
328
  /* EXPORT */
269
329
 
package/src/types.ts CHANGED
@@ -1,37 +1,37 @@
1
1
 
2
- /* TYPES */
2
+ /* MAIN */
3
3
 
4
- type Callback = ( error: Exception | void ) => any;
4
+ type Callback = ( error: Exception | void ) => void;
5
5
 
6
- type Data = Buffer | string | undefined;
6
+ type Data = Uint8Array | string | undefined;
7
7
 
8
8
  type Disposer = () => void;
9
9
 
10
- type Exception = NodeJS.ErrnoException;
10
+ type Encoding = 'ascii' | 'base64' | 'binary' | 'hex' | 'latin1' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2';
11
11
 
12
- type FN<Arguments extends any[] = any[], Return = any> = ( ...args: Arguments ) => Return;
12
+ type Exception = NodeJS.ErrnoException;
13
13
 
14
14
  type Path = string;
15
15
 
16
16
  type ReadOptions = {
17
- encoding?: string | null,
17
+ encoding?: Encoding | null,
18
18
  mode?: string | number | false,
19
19
  timeout?: number
20
20
  };
21
21
 
22
22
  type WriteOptions = {
23
23
  chown?: { gid: number, uid: number } | false,
24
- encoding?: string | null,
24
+ encoding?: Encoding | null,
25
25
  fsync?: boolean,
26
26
  fsyncWait?: boolean,
27
27
  mode?: string | number | false,
28
28
  schedule?: ( filePath: string ) => Promise<Disposer>,
29
29
  timeout?: number,
30
30
  tmpCreate?: ( filePath: string ) => string,
31
- tmpCreated?: ( filePath: string ) => any,
31
+ tmpCreated?: ( filePath: string ) => void,
32
32
  tmpPurge?: boolean
33
33
  };
34
34
 
35
35
  /* EXPORT */
36
36
 
37
- export {Callback, Data, Disposer, Exception, FN, Path, ReadOptions, WriteOptions};
37
+ export type {Callback, Data, Disposer, Encoding, Exception, Path, ReadOptions, WriteOptions};
package/src/utils/lang.ts CHANGED
@@ -1,28 +1,34 @@
1
1
 
2
- /* LANG */
2
+ /* IMPORT */
3
3
 
4
- const Lang = {
4
+ import type {Exception} from '../types';
5
5
 
6
- isFunction: ( x: any ): x is Function => {
6
+ /* MAIN */
7
7
 
8
- return typeof x === 'function';
8
+ const isException = ( value: unknown ): value is Exception => {
9
9
 
10
- },
10
+ return ( value instanceof Error ) && ( 'code' in value );
11
11
 
12
- isString: ( x: any ): x is string => {
12
+ };
13
+
14
+ const isFunction = ( value: unknown ): value is Function => {
15
+
16
+ return ( typeof value === 'function' );
13
17
 
14
- return typeof x === 'string';
18
+ };
19
+
20
+ const isString = ( value: unknown ): value is string => {
15
21
 
16
- },
22
+ return ( typeof value === 'string' );
17
23
 
18
- isUndefined: ( x: any ): x is undefined => {
24
+ };
19
25
 
20
- return typeof x === 'undefined';
26
+ const isUndefined = ( value: unknown ): value is undefined => {
21
27
 
22
- }
28
+ return ( value === undefined );
23
29
 
24
30
  };
25
31
 
26
32
  /* EXPORT */
27
33
 
28
- export default Lang;
34
+ export {isException, isFunction, isString, isUndefined};
@@ -1,18 +1,20 @@
1
1
 
2
2
  /* IMPORT */
3
3
 
4
- import {Disposer} from '../types';
4
+ import type {Disposer} from '../types';
5
5
 
6
- /* VARIABLES */
6
+ /* HELPERS */
7
7
 
8
8
  const Queues: Record<string, Function[] | undefined> = {};
9
9
 
10
- /* SCHEDULER */
10
+ /* MAIN */
11
11
 
12
12
  //TODO: Maybe publish this as a standalone package
13
13
 
14
14
  const Scheduler = {
15
15
 
16
+ /* API */
17
+
16
18
  next: ( id: string ): void => {
17
19
 
18
20
  const queue = Queues[id];