Skip to content

Commit ba7f5bc

Browse files
committed
feat: scheduleExit() function
1 parent c681a73 commit ba7f5bc

File tree

7 files changed

+52
-4
lines changed

7 files changed

+52
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,13 @@ Parts of the code have been overhauled, and some features have been added:
1111
- Added [`takeRandomItemIndex()`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#function-takerandomitemindex), as a mutating counterpart to [`randomItemIndex()`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#function-randomitemindex)
1212
- Added [`truncStr()`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#function-truncstr) to truncate a string to a given length, optionally adding an ellipsis.
1313
- Added [`valsWithin()`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#function-valswithin) to check if two values, rounded at the given decimal, are within a certain range of each other.
14+
- Added [`scheduleExit()`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#function-scheduleexit) to schedule a Node/Deno process to exit with the given code, after the microtask queue is empty or after the given timeout.
1415
- **BREAKING CHANGES:**
15-
- Reworked DataStore
16+
- Reworked `DataStore`
1617
- The constructor now needs an `engine` property that is an instance of a [`DataStoreEngine`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#class-datastoreengine.)
1718
- Encoding with `deflate-raw` will now be enabled by default. Set `compressionFormat: null` to disable it and restore the previous behavior.
1819
- Added [`DataStoreEngine` class](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#class-datastoreengine) with two implementations available out-of-the-box; [`FileStorageEngine`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#class-filestorageengine) and [`BrowserStorageEngine`](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#class-browserstorageengine), for Node/Deno and browser environments respectively. Userscripts need to use [`BrowserStorageEngine`.](https://github.com/Sv443-Network/CoreUtils/blob/main/docs.md#class-browserstorageengine)
1920
- Added shorthand property `compressionFormat` as an alternative to the properties `encodeData` and `decodeData`
2021
- The global key `__ds_fmt_ver` will now contain a global version number for DataStore-internal format integrity.
21-
- Renamed `ab2str()` to `abtoa()` and `str2ab()` to `atoab()` to match `btoa()` and `atob()`
22+
- Renamed `ab2str()` to `abtoa()` and `str2ab()` to `atoab()` to match the naming of `atob()` and `btoa()`
2223
- Renamed `purifyObj()` to `pureObj()`

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ Intended to be used in conjunction with [`@sv443-network/userutils`](https://git
7979
- 🟣 [`function pureObj()`](./docs.md#function-pureobj) - Applies an object's props to a null object (object without prototype chain) or just returns a new null object
8080
- 🟣 [`function setImmediateInterval()`](./docs.md#function-setimmediateinterval) - Like `setInterval()`, but instantly calls the callback and supports passing an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
8181
- 🟣 [`function setImmediateTimeoutLoop()`](./docs.md#function-setimmediatetimeoutloop) - Like a recursive `setTimeout()` loop, but instantly calls the callback and supports passing an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
82+
- 🟣 [`function scheduleExit()`](./docs.md#function-scheduleexit) - Schedules a process exit after the next event loop tick, to allow operations like IO writes to finish.
8283
- [**NanoEmitter:**](./docs.md#nanoemitter)
8384
- 🟧 [`class NanoEmitter`](./docs.md#class-nanoemitter) - Simple, lightweight event emitter class that can be used in both FP and OOP, inspired by [`EventEmitter` from `node:events`](https://nodejs.org/api/events.html#class-eventemitter), based on [`nanoevents`](https://npmjs.com/package/nanoevents)
8485
- 🔷 [`type NanoEmitterOptions`](./docs.md#type-nanoemitteroptions) - Options for the [`NanoEmitter` class](./docs.md#class-nanoemitter)

docs.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ For submitting bug reports or feature requests, please use the [GitHub issue tra
9797
- 🟣 [`function pureObj()`](#function-pureobj) - Applies an object's props to a null object (object without prototype chain) or just returns a new null object
9898
- 🟣 [`function setImmediateInterval()`](#function-setimmediateinterval) - Like `setInterval()`, but instantly calls the callback and supports passing an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
9999
- 🟣 [`function setImmediateTimeoutLoop()`](#function-setimmediatetimeoutloop) - Like a recursive `setTimeout()` loop, but instantly calls the callback and supports passing an [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal)
100+
- 🟣 [`function scheduleExit()`](#function-scheduleexit) - Schedules a process exit after the next event loop tick, to allow operations like IO writes to finish.
100101
- [**NanoEmitter:**](#nanoemitter)
101102
- 🟧 [`class NanoEmitter`](#class-nanoemitter) - Simple, lightweight event emitter class that can be used in both FP and OOP, inspired by [`EventEmitter` from `node:events`](https://nodejs.org/api/events.html#class-eventemitter), based on [`nanoevents`](https://npmjs.com/package/nanoevents)
102103
- 🔷 [`type NanoEmitterOptions`](#type-nanoemitteroptions) - Options for the [`NanoEmitter` class](#class-nanoemitter)
@@ -2495,6 +2496,20 @@ abortButton.addEventListener("click", () => {
24952496
```
24962497
</details>
24972498

2499+
<br>
2500+
2501+
### `function scheduleExit()`
2502+
Signature:
2503+
```ts
2504+
function scheduleExit(code?: number, timeout?: number): void;
2505+
```
2506+
2507+
Schedules the process to exit with the given exit code after a timeout, in both Node.js and Deno, but not in the browser.
2508+
This is useful for allowing the currently queued microtasks to finish before exiting the process. This can prevent data loss or corruption when writing to files or databases, for example.
2509+
If no exit code is given, it will default to 0 (success). Set it to 1 to indicate an error.
2510+
If no timeout is given, the process will exit immediately after the current microtask queue is cleared (0ms).
2511+
If a timeout is given, the process will exit after the timeout has elapsed, regardless of whether there are still microtasks in the queue.
2512+
24982513
<br><br>
24992514

25002515

lib/misc.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,3 +178,25 @@ export function setImmediateTimeoutLoop(
178178
signal?.addEventListener("abort", cleanup);
179179
loop();
180180
}
181+
182+
/**
183+
* Schedules an exit of the current process after the next event loop tick, in order to allow any pending operations like IO writes to complete.
184+
* Works on both Node.js and Deno, but will not work in browser environments.
185+
* @param code The exit code to use, defaults to 0 (success)
186+
* @param timeout The time in milliseconds to wait before exiting, defaults to 0 (exit on the next event loop tick)
187+
* @throws An error if no exit method is available (e.g. in browser environments)
188+
*/
189+
export function scheduleExit(code: number = 0, timeout = 0): void {
190+
if(timeout < 0)
191+
throw new TypeError("Timeout must be a non-negative number");
192+
193+
let exit: (() => void) | undefined;
194+
if(typeof process !== "undefined" && "exit" in process)
195+
exit = () => process.exit(code);
196+
else if(typeof Deno !== "undefined" && "exit" in Deno)
197+
exit = () => Deno.exit(code);
198+
else
199+
throw new Error("Cannot exit the process, no exit method available");
200+
201+
setTimeout(exit, timeout);
202+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
"@eslint/js": "^9.28.0",
6060
"@swc/core": "^1.11.29",
6161
"@testing-library/dom": "^10.4.0",
62+
"@types/deno": "^2.3.0",
6263
"@types/node": "^22.15.29",
6364
"@types/tx2": "^1.0.3",
6465
"@typescript-eslint/eslint-plugin": "^8.33.0",

pnpm-lock.yaml

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tools/fix-dts.mts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { readdir, readFile, writeFile, lstat } from "node:fs/promises";
1+
import { readdir, readFile, writeFile, stat } from "node:fs/promises";
22
import { join, resolve } from "node:path";
33
import { styleText } from "node:util";
44

@@ -24,7 +24,7 @@ async function processRecursive(directory: string): Promise<void> {
2424

2525
for(const file of files) {
2626
const fullPath = join(directory, file);
27-
const stats = await lstat(fullPath);
27+
const stats = await stat(fullPath);
2828

2929
if(stats.isDirectory())
3030
await processRecursive(fullPath);

0 commit comments

Comments
 (0)