atomically 2.0.2 → 2.0.4
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/.nyc_output/09ad542c-5435-4970-ae9c-d3aca17cc8e0.json +1 -0
- package/.nyc_output/5c74a299-649e-4bcb-894e-d3d7bc61ba96.json +1 -0
- package/.nyc_output/aec5ef7b-c224-4fa5-a830-b12bd1873072.json +1 -0
- package/.nyc_output/e5566b61-47b7-4c05-8e8b-36c7f809df3e.json +1 -0
- package/.nyc_output/f6bca65a-d147-4d4d-9fbf-a307181ea52f.json +1 -0
- package/.nyc_output/processinfo/09ad542c-5435-4970-ae9c-d3aca17cc8e0.json +1 -0
- package/.nyc_output/processinfo/5c74a299-649e-4bcb-894e-d3d7bc61ba96.json +1 -0
- package/.nyc_output/processinfo/aec5ef7b-c224-4fa5-a830-b12bd1873072.json +1 -0
- package/.nyc_output/processinfo/e5566b61-47b7-4c05-8e8b-36c7f809df3e.json +1 -0
- package/.nyc_output/processinfo/f6bca65a-d147-4d4d-9fbf-a307181ea52f.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +25 -21
- package/dist/types.d.ts +0 -1
- package/dist/utils/lang.d.ts +2 -2
- package/dist/utils/scheduler.d.ts +1 -1
- package/dist/utils/temp.d.ts +1 -1
- package/dist/utils/temp.js +1 -1
- package/package.json +10 -9
- package/.editorconfig +0 -10
- package/src/constants.ts +0 -39
- package/src/index.ts +0 -330
- package/src/types.ts +0 -37
- package/src/utils/lang.ts +0 -34
- package/src/utils/scheduler.ts +0 -62
- package/src/utils/temp.ts +0 -102
- package/tasks/benchmark.js +0 -76
- package/test/basic.cjs +0 -508
- package/test/concurrency.cjs +0 -151
- package/test/integration.cjs +0 -289
- package/tsconfig.json +0 -3
package/src/index.ts
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/* IMPORT */
|
|
3
|
-
|
|
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
|
-
import Scheduler from './utils/scheduler';
|
|
9
|
-
import Temp from './utils/temp';
|
|
10
|
-
import type {Callback, Data, Disposer, Encoding, Path, ReadOptions, WriteOptions} from './types';
|
|
11
|
-
|
|
12
|
-
/* MAIN */
|
|
13
|
-
|
|
14
|
-
function readFile ( filePath: Path, options: Encoding | ReadOptions & { encoding: string } ): Promise<string>;
|
|
15
|
-
function readFile ( filePath: Path, options?: ReadOptions ): Promise<Buffer>;
|
|
16
|
-
function readFile ( filePath: Path, options: Encoding | ReadOptions = DEFAULT_READ_OPTIONS ): Promise<Buffer | string> {
|
|
17
|
-
|
|
18
|
-
if ( isString ( options ) ) return readFile ( filePath, { encoding: options } );
|
|
19
|
-
|
|
20
|
-
const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_ASYNC ) || -1 );
|
|
21
|
-
|
|
22
|
-
return fs.retry.readFile ( timeout )( filePath, options );
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function readFileSync ( filePath: Path, options: Encoding | ReadOptions & { encoding: string } ): string;
|
|
27
|
-
function readFileSync ( filePath: Path, options?: ReadOptions ): Buffer;
|
|
28
|
-
function readFileSync ( filePath: Path, options: Encoding | ReadOptions = DEFAULT_READ_OPTIONS ): Buffer | string {
|
|
29
|
-
|
|
30
|
-
if ( isString ( options ) ) return readFileSync ( filePath, { encoding: options } );
|
|
31
|
-
|
|
32
|
-
const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_SYNC ) || -1 );
|
|
33
|
-
|
|
34
|
-
return fs.retry.readFileSync ( timeout )( filePath, options );
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
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> {
|
|
41
|
-
|
|
42
|
-
if ( isFunction ( options ) ) return writeFile ( filePath, data, DEFAULT_WRITE_OPTIONS, options );
|
|
43
|
-
|
|
44
|
-
const promise = writeFileAsync ( filePath, data, options );
|
|
45
|
-
|
|
46
|
-
if ( callback ) promise.then ( callback, callback );
|
|
47
|
-
|
|
48
|
-
return promise;
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function writeFileAsync ( filePath: Path, data: Data, options: Encoding | WriteOptions = DEFAULT_WRITE_OPTIONS ): Promise<void> {
|
|
53
|
-
|
|
54
|
-
if ( isString ( options ) ) return writeFileAsync ( filePath, data, { encoding: options } );
|
|
55
|
-
|
|
56
|
-
const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_ASYNC ) || -1 );
|
|
57
|
-
|
|
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;
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
|
|
66
|
-
if ( options.schedule ) schedulerCustomDisposer = await options.schedule ( filePath );
|
|
67
|
-
|
|
68
|
-
schedulerDisposer = await Scheduler.schedule ( filePath );
|
|
69
|
-
|
|
70
|
-
const filePathReal = await fs.attempt.realpath ( filePath );
|
|
71
|
-
const filePathExists = !!filePathReal;
|
|
72
|
-
|
|
73
|
-
filePath = filePathReal || filePath;
|
|
74
|
-
|
|
75
|
-
[tempPath, tempDisposer] = Temp.get ( filePath, options.tmpCreate || Temp.create, !( options.tmpPurge === false ) );
|
|
76
|
-
|
|
77
|
-
const useStatChown = IS_POSIX && isUndefined ( options.chown );
|
|
78
|
-
const useStatMode = isUndefined ( options.mode );
|
|
79
|
-
|
|
80
|
-
if ( filePathExists && ( useStatChown || useStatMode ) ) {
|
|
81
|
-
|
|
82
|
-
const stats = await fs.attempt.stat ( filePath );
|
|
83
|
-
|
|
84
|
-
if ( stats ) {
|
|
85
|
-
|
|
86
|
-
options = { ...options };
|
|
87
|
-
|
|
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;
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
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 );
|
|
116
|
-
|
|
117
|
-
if ( options.tmpCreated ) {
|
|
118
|
-
|
|
119
|
-
options.tmpCreated ( tempPath );
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if ( isString ( data ) ) {
|
|
124
|
-
|
|
125
|
-
await fs.retry.write ( timeout )( fd, data, 0, options.encoding || DEFAULT_ENCODING );
|
|
126
|
-
|
|
127
|
-
} else if ( !isUndefined ( data ) ) {
|
|
128
|
-
|
|
129
|
-
await fs.retry.write ( timeout )( fd, data, 0, data.length, 0 );
|
|
130
|
-
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if ( options.fsync !== false ) {
|
|
134
|
-
|
|
135
|
-
if ( options.fsyncWait !== false ) {
|
|
136
|
-
|
|
137
|
-
await fs.retry.fsync ( timeout )( fd );
|
|
138
|
-
|
|
139
|
-
} else {
|
|
140
|
-
|
|
141
|
-
fs.attempt.fsync ( fd );
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
await fs.retry.close ( timeout )( fd );
|
|
148
|
-
|
|
149
|
-
fd = null;
|
|
150
|
-
|
|
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
|
-
}
|
|
156
|
-
|
|
157
|
-
if ( options.mode && options.mode !== DEFAULT_FILE_MODE ) {
|
|
158
|
-
|
|
159
|
-
await fs.attempt.chmod ( tempPath, options.mode );
|
|
160
|
-
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
try {
|
|
164
|
-
|
|
165
|
-
await fs.retry.rename ( timeout )( tempPath, filePath );
|
|
166
|
-
|
|
167
|
-
} catch ( error: unknown ) {
|
|
168
|
-
|
|
169
|
-
if ( !isException ( error ) ) throw error;
|
|
170
|
-
|
|
171
|
-
if ( error.code !== 'ENAMETOOLONG' ) throw error;
|
|
172
|
-
|
|
173
|
-
await fs.retry.rename ( timeout )( tempPath, Temp.truncate ( filePath ) );
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
tempDisposer ();
|
|
178
|
-
|
|
179
|
-
tempPath = null;
|
|
180
|
-
|
|
181
|
-
} finally {
|
|
182
|
-
|
|
183
|
-
if ( fd ) await fs.attempt.close ( fd );
|
|
184
|
-
|
|
185
|
-
if ( tempPath ) Temp.purge ( tempPath );
|
|
186
|
-
|
|
187
|
-
if ( schedulerCustomDisposer ) schedulerCustomDisposer ();
|
|
188
|
-
|
|
189
|
-
if ( schedulerDisposer ) schedulerDisposer ();
|
|
190
|
-
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function writeFileSync ( filePath: Path, data: Data, options: Encoding | WriteOptions = DEFAULT_WRITE_OPTIONS ): void {
|
|
196
|
-
|
|
197
|
-
if ( isString ( options ) ) return writeFileSync ( filePath, data, { encoding: options } );
|
|
198
|
-
|
|
199
|
-
const timeout = Date.now () + ( ( options.timeout ?? DEFAULT_TIMEOUT_SYNC ) || -1 );
|
|
200
|
-
|
|
201
|
-
let tempDisposer: Disposer | null = null;
|
|
202
|
-
let tempPath: string | null = null;
|
|
203
|
-
let fd: number | null = null;
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
|
|
207
|
-
const filePathReal = fs.attempt.realpathSync ( filePath );
|
|
208
|
-
const filePathExists = !!filePathReal;
|
|
209
|
-
|
|
210
|
-
filePath = filePathReal || filePath;
|
|
211
|
-
|
|
212
|
-
[tempPath, tempDisposer] = Temp.get ( filePath, options.tmpCreate || Temp.create, !( options.tmpPurge === false ) );
|
|
213
|
-
|
|
214
|
-
const useStatChown = IS_POSIX && isUndefined ( options.chown );
|
|
215
|
-
const useStatMode = isUndefined ( options.mode );
|
|
216
|
-
|
|
217
|
-
if ( filePathExists && ( useStatChown || useStatMode ) ) {
|
|
218
|
-
|
|
219
|
-
const stats = fs.attempt.statSync ( filePath );
|
|
220
|
-
|
|
221
|
-
if ( stats ) {
|
|
222
|
-
|
|
223
|
-
options = { ...options };
|
|
224
|
-
|
|
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;
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
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 );
|
|
253
|
-
|
|
254
|
-
if ( options.tmpCreated ) {
|
|
255
|
-
|
|
256
|
-
options.tmpCreated ( tempPath );
|
|
257
|
-
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if ( isString ( data ) ) {
|
|
261
|
-
|
|
262
|
-
fs.retry.writeSync ( timeout )( fd, data, 0, options.encoding || DEFAULT_ENCODING );
|
|
263
|
-
|
|
264
|
-
} else if ( !isUndefined ( data ) ) {
|
|
265
|
-
|
|
266
|
-
fs.retry.writeSync ( timeout )( fd, data, 0, data.length, 0 );
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if ( options.fsync !== false ) {
|
|
271
|
-
|
|
272
|
-
if ( options.fsyncWait !== false ) {
|
|
273
|
-
|
|
274
|
-
fs.retry.fsyncSync ( timeout )( fd );
|
|
275
|
-
|
|
276
|
-
} else {
|
|
277
|
-
|
|
278
|
-
fs.attempt.fsync ( fd );
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
fs.retry.closeSync ( timeout )( fd );
|
|
285
|
-
|
|
286
|
-
fd = null;
|
|
287
|
-
|
|
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
|
-
}
|
|
293
|
-
|
|
294
|
-
if ( options.mode && options.mode !== DEFAULT_FILE_MODE ) {
|
|
295
|
-
|
|
296
|
-
fs.attempt.chmodSync ( tempPath, options.mode );
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
try {
|
|
301
|
-
|
|
302
|
-
fs.retry.renameSync ( timeout )( tempPath, filePath );
|
|
303
|
-
|
|
304
|
-
} catch ( error: unknown ) {
|
|
305
|
-
|
|
306
|
-
if ( !isException ( error ) ) throw error;
|
|
307
|
-
|
|
308
|
-
if ( error.code !== 'ENAMETOOLONG' ) throw error;
|
|
309
|
-
|
|
310
|
-
fs.retry.renameSync ( timeout )( tempPath, Temp.truncate ( filePath ) );
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
tempDisposer ();
|
|
315
|
-
|
|
316
|
-
tempPath = null;
|
|
317
|
-
|
|
318
|
-
} finally {
|
|
319
|
-
|
|
320
|
-
if ( fd ) fs.attempt.closeSync ( fd );
|
|
321
|
-
|
|
322
|
-
if ( tempPath ) Temp.purge ( tempPath );
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/* EXPORT */
|
|
329
|
-
|
|
330
|
-
export {readFile, readFileSync, writeFile, writeFileSync};
|
package/src/types.ts
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/* MAIN */
|
|
3
|
-
|
|
4
|
-
type Callback = ( error: Exception | void ) => void;
|
|
5
|
-
|
|
6
|
-
type Data = Uint8Array | string | undefined;
|
|
7
|
-
|
|
8
|
-
type Disposer = () => void;
|
|
9
|
-
|
|
10
|
-
type Encoding = 'ascii' | 'base64' | 'binary' | 'hex' | 'latin1' | 'utf8' | 'utf-8' | 'utf16le' | 'ucs2' | 'ucs-2';
|
|
11
|
-
|
|
12
|
-
type Exception = NodeJS.ErrnoException;
|
|
13
|
-
|
|
14
|
-
type Path = string;
|
|
15
|
-
|
|
16
|
-
type ReadOptions = {
|
|
17
|
-
encoding?: Encoding | null,
|
|
18
|
-
mode?: string | number | false,
|
|
19
|
-
timeout?: number
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
type WriteOptions = {
|
|
23
|
-
chown?: { gid: number, uid: number } | false,
|
|
24
|
-
encoding?: Encoding | null,
|
|
25
|
-
fsync?: boolean,
|
|
26
|
-
fsyncWait?: boolean,
|
|
27
|
-
mode?: string | number | false,
|
|
28
|
-
schedule?: ( filePath: string ) => Promise<Disposer>,
|
|
29
|
-
timeout?: number,
|
|
30
|
-
tmpCreate?: ( filePath: string ) => string,
|
|
31
|
-
tmpCreated?: ( filePath: string ) => void,
|
|
32
|
-
tmpPurge?: boolean
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
/* EXPORT */
|
|
36
|
-
|
|
37
|
-
export type {Callback, Data, Disposer, Encoding, Exception, Path, ReadOptions, WriteOptions};
|
package/src/utils/lang.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/* IMPORT */
|
|
3
|
-
|
|
4
|
-
import type {Exception} from '../types';
|
|
5
|
-
|
|
6
|
-
/* MAIN */
|
|
7
|
-
|
|
8
|
-
const isException = ( value: unknown ): value is Exception => {
|
|
9
|
-
|
|
10
|
-
return ( value instanceof Error ) && ( 'code' in value );
|
|
11
|
-
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
const isFunction = ( value: unknown ): value is Function => {
|
|
15
|
-
|
|
16
|
-
return ( typeof value === 'function' );
|
|
17
|
-
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const isString = ( value: unknown ): value is string => {
|
|
21
|
-
|
|
22
|
-
return ( typeof value === 'string' );
|
|
23
|
-
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const isUndefined = ( value: unknown ): value is undefined => {
|
|
27
|
-
|
|
28
|
-
return ( value === undefined );
|
|
29
|
-
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/* EXPORT */
|
|
33
|
-
|
|
34
|
-
export {isException, isFunction, isString, isUndefined};
|
package/src/utils/scheduler.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/* IMPORT */
|
|
3
|
-
|
|
4
|
-
import type {Disposer} from '../types';
|
|
5
|
-
|
|
6
|
-
/* HELPERS */
|
|
7
|
-
|
|
8
|
-
const Queues: Record<string, Function[] | undefined> = {};
|
|
9
|
-
|
|
10
|
-
/* MAIN */
|
|
11
|
-
|
|
12
|
-
//TODO: Maybe publish this as a standalone package
|
|
13
|
-
|
|
14
|
-
const Scheduler = {
|
|
15
|
-
|
|
16
|
-
/* API */
|
|
17
|
-
|
|
18
|
-
next: ( id: string ): void => {
|
|
19
|
-
|
|
20
|
-
const queue = Queues[id];
|
|
21
|
-
|
|
22
|
-
if ( !queue ) return;
|
|
23
|
-
|
|
24
|
-
queue.shift ();
|
|
25
|
-
|
|
26
|
-
const job = queue[0];
|
|
27
|
-
|
|
28
|
-
if ( job ) {
|
|
29
|
-
|
|
30
|
-
job ( () => Scheduler.next ( id ) );
|
|
31
|
-
|
|
32
|
-
} else {
|
|
33
|
-
|
|
34
|
-
delete Queues[id];
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
},
|
|
39
|
-
|
|
40
|
-
schedule: ( id: string ): Promise<Disposer> => {
|
|
41
|
-
|
|
42
|
-
return new Promise ( resolve => {
|
|
43
|
-
|
|
44
|
-
let queue = Queues[id];
|
|
45
|
-
|
|
46
|
-
if ( !queue ) queue = Queues[id] = [];
|
|
47
|
-
|
|
48
|
-
queue.push ( resolve );
|
|
49
|
-
|
|
50
|
-
if ( queue.length > 1 ) return;
|
|
51
|
-
|
|
52
|
-
resolve ( () => Scheduler.next ( id ) );
|
|
53
|
-
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
/* EXPORT */
|
|
61
|
-
|
|
62
|
-
export default Scheduler;
|
package/src/utils/temp.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/* IMPORT */
|
|
3
|
-
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import fs from 'stubborn-fs';
|
|
6
|
-
import whenExit from 'when-exit';
|
|
7
|
-
import {LIMIT_BASENAME_LENGTH} from '../constants';
|
|
8
|
-
import type {Disposer} from '../types';
|
|
9
|
-
|
|
10
|
-
/* MAIN */
|
|
11
|
-
|
|
12
|
-
//TODO: Maybe publish this as a standalone package
|
|
13
|
-
|
|
14
|
-
const Temp = {
|
|
15
|
-
|
|
16
|
-
/* VARIABLES */
|
|
17
|
-
|
|
18
|
-
store: <Record<string, boolean>> {}, // filePath => purge
|
|
19
|
-
|
|
20
|
-
/* API */
|
|
21
|
-
|
|
22
|
-
create: ( filePath: string ): string => {
|
|
23
|
-
|
|
24
|
-
const randomness = `000000${Math.floor ( Math.random () * 16777215 ).toString ( 16 )}`.slice ( -6 ); // 6 random-enough hex characters
|
|
25
|
-
const timestamp = Date.now ().toString ().slice ( -10 ); // 10 precise timestamp digits
|
|
26
|
-
const prefix = 'tmp-';
|
|
27
|
-
const suffix = `.${prefix}${timestamp}${randomness}`;
|
|
28
|
-
const tempPath = `${filePath}${suffix}`;
|
|
29
|
-
|
|
30
|
-
return tempPath;
|
|
31
|
-
|
|
32
|
-
},
|
|
33
|
-
|
|
34
|
-
get: ( filePath: string, creator: ( filePath: string ) => string, purge: boolean = true ): [string, Disposer] => {
|
|
35
|
-
|
|
36
|
-
const tempPath = Temp.truncate ( creator ( filePath ) );
|
|
37
|
-
|
|
38
|
-
if ( tempPath in Temp.store ) return Temp.get ( filePath, creator, purge ); // Collision found, try again
|
|
39
|
-
|
|
40
|
-
Temp.store[tempPath] = purge;
|
|
41
|
-
|
|
42
|
-
const disposer = () => delete Temp.store[tempPath];
|
|
43
|
-
|
|
44
|
-
return [tempPath, disposer];
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
purge: ( filePath: string ): void => {
|
|
49
|
-
|
|
50
|
-
if ( !Temp.store[filePath] ) return;
|
|
51
|
-
|
|
52
|
-
delete Temp.store[filePath];
|
|
53
|
-
|
|
54
|
-
fs.attempt.unlink ( filePath );
|
|
55
|
-
|
|
56
|
-
},
|
|
57
|
-
|
|
58
|
-
purgeSync: ( filePath: string ): void => {
|
|
59
|
-
|
|
60
|
-
if ( !Temp.store[filePath] ) return;
|
|
61
|
-
|
|
62
|
-
delete Temp.store[filePath];
|
|
63
|
-
|
|
64
|
-
fs.attempt.unlinkSync ( filePath );
|
|
65
|
-
|
|
66
|
-
},
|
|
67
|
-
|
|
68
|
-
purgeSyncAll: (): void => {
|
|
69
|
-
|
|
70
|
-
for ( const filePath in Temp.store ) {
|
|
71
|
-
|
|
72
|
-
Temp.purgeSync ( filePath );
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
truncate: ( filePath: string ): string => { // Truncating paths to avoid getting an "ENAMETOOLONG" error //FIXME: This doesn't really always work, the actual filesystem limits must be detected for this to be implemented correctly
|
|
79
|
-
|
|
80
|
-
const basename = path.basename ( filePath );
|
|
81
|
-
|
|
82
|
-
if ( basename.length <= LIMIT_BASENAME_LENGTH ) return filePath; //FIXME: Rough and quick attempt at detecting ok lengths
|
|
83
|
-
|
|
84
|
-
const truncable = /^(\.?)(.*?)((?:\.[^.]+)?(?:\.tmp-\d{10}[a-f0-9]{6})?)$/.exec ( basename );
|
|
85
|
-
|
|
86
|
-
if ( !truncable ) 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
|
|
87
|
-
|
|
88
|
-
const truncationLength = basename.length - LIMIT_BASENAME_LENGTH;
|
|
89
|
-
|
|
90
|
-
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
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/* INIT */
|
|
97
|
-
|
|
98
|
-
whenExit ( Temp.purgeSyncAll ); // Ensuring purgeable temp files are purged on exit
|
|
99
|
-
|
|
100
|
-
/* EXPORT */
|
|
101
|
-
|
|
102
|
-
export default Temp;
|
package/tasks/benchmark.js
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
/* IMPORT */
|
|
3
|
-
|
|
4
|
-
import {randomUUID} from 'node:crypto';
|
|
5
|
-
import fs from 'node:fs';
|
|
6
|
-
import os from 'node:os';
|
|
7
|
-
import path from 'node:path';
|
|
8
|
-
import {setTimeout as delay} from 'node:timers/promises';
|
|
9
|
-
import writeFileAtomic from 'write-file-atomic';
|
|
10
|
-
import {writeFile, writeFileSync} from '../dist/index.js';
|
|
11
|
-
|
|
12
|
-
/* MAIN */
|
|
13
|
-
|
|
14
|
-
const TEMP = os.tmpdir ();
|
|
15
|
-
const UUID = randomUUID ();
|
|
16
|
-
const DST = i => path.join ( TEMP, `atomically-${UUID}-temp-${i}.txt` );
|
|
17
|
-
const ITERATIONS = 250;
|
|
18
|
-
|
|
19
|
-
const runSingleAsync = async ( name, fn, buffer, options ) => {
|
|
20
|
-
console.time ( name );
|
|
21
|
-
for ( let i = 0; i < ITERATIONS; i++ ) {
|
|
22
|
-
await fn ( DST ( i ), buffer, options );
|
|
23
|
-
}
|
|
24
|
-
console.timeEnd ( name );
|
|
25
|
-
await delay ( 1000 );
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const runSingleSync = async ( name, fn, buffer, options ) => {
|
|
29
|
-
console.time ( name );
|
|
30
|
-
for ( let i = 0; i < ITERATIONS; i++ ) {
|
|
31
|
-
fn ( DST ( i ), buffer, options );
|
|
32
|
-
}
|
|
33
|
-
console.timeEnd ( name );
|
|
34
|
-
await delay ( 1000 );
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
const runAllDummy = () => { // Preparation run
|
|
38
|
-
runSingleSync ( 'dummy', fs.writeFileSync, '' );
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const runAllAsync = async ( name, buffer ) => {
|
|
42
|
-
await runSingleAsync ( `${name} -> async -> write-file-atomic`, writeFileAtomic, buffer, { mode: 0o666 } );
|
|
43
|
-
await runSingleAsync ( `${name} -> async -> write-file-atomic (faster)`, writeFileAtomic, buffer );
|
|
44
|
-
await runSingleAsync ( `${name} -> async -> write-file-atomic (fastest)`, writeFileAtomic, buffer, { fsync: false } );
|
|
45
|
-
await runSingleAsync ( `${name} -> async -> atomically`, writeFile, buffer );
|
|
46
|
-
await runSingleAsync ( `${name} -> async -> atomically (faster)`, writeFile, buffer, { mode: false, chown: false, fsyncWait: false } );
|
|
47
|
-
await runSingleAsync ( `${name} -> async -> atomically (fastest)`, writeFile, buffer, { mode: false, chown: false, fsync: false } );
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const runAllSync = ( name, buffer ) => {
|
|
51
|
-
runSingleSync ( `${name} -> sync -> write-file-atomic`, writeFileAtomic.sync, buffer, { mode: 0o666 } );
|
|
52
|
-
runSingleSync ( `${name} -> sync -> write-file-atomic (faster)`, writeFileAtomic.sync, buffer );
|
|
53
|
-
runSingleSync ( `${name} -> sync -> write-file-atomic (fastest)`, writeFileAtomic.sync, buffer, { fsync: false } );
|
|
54
|
-
runSingleSync ( `${name} -> sync -> atomically`, writeFileSync, buffer );
|
|
55
|
-
runSingleSync ( `${name} -> sync -> atomically (faster)`, writeFileSync, buffer, { mode: false, chown: false, fsyncWait: false } );
|
|
56
|
-
runSingleSync ( `${name} -> sync -> atomically (fastest)`, writeFileSync, buffer, { mode: false, chown: false, fsync: false } );
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const runAll = async ( name, buffer ) => {
|
|
60
|
-
await runAllAsync ( name, buffer );
|
|
61
|
-
console.log ( '-------------------' );
|
|
62
|
-
runAllSync ( name, buffer );
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const run = async () => {
|
|
66
|
-
runAllDummy ();
|
|
67
|
-
console.log ( '===================' );
|
|
68
|
-
await runAll ( '100kb', Buffer.allocUnsafe ( 100 * 1024 ) );
|
|
69
|
-
console.log ( '===================' );
|
|
70
|
-
await runAll ( '10kb', Buffer.allocUnsafe ( 10 * 1024 ) );
|
|
71
|
-
console.log ( '===================' );
|
|
72
|
-
await runAll ( '1kb', Buffer.allocUnsafe ( 1024 ) );
|
|
73
|
-
console.log ( '===================' );
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
run ();
|