@radically-straightforward/utilities 0.0.3 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.1 · 2024-01-09
4
+
5
+ - Added `log()`.
6
+
7
+ ## 1.0.0 · 2024-01-06
8
+
9
+ - Added `backgroundJob()`.
10
+ - Added `sleep()`.
11
+ - Added `randomString()`.
12
+
3
13
  ## 0.0.3 · 2024-01-05
4
14
 
5
15
  - Made `intern()` more strict in terms of types and provide auxiliary types for it.
package/README.md CHANGED
@@ -16,6 +16,94 @@ import * as utilities from "@radically-straightforward/utilities";
16
16
 
17
17
  <!-- DOCUMENTATION START: ./source/index.mts -->
18
18
 
19
+ ### `backgroundJob()`
20
+
21
+ ```typescript
22
+ export function backgroundJob(
23
+ {
24
+ interval,
25
+ intervalVariance = 0.1,
26
+ }: {
27
+ interval: number;
28
+ intervalVariance?: number;
29
+ },
30
+ job: () => void | Promise<void>,
31
+ ): {
32
+ run: () => void;
33
+ stop: () => void;
34
+ };
35
+ ```
36
+
37
+ Start a background job that runs every `interval`.
38
+
39
+ This is different from `setInterval()` in the following ways:
40
+
41
+ 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
42
+
43
+ ```
44
+ setInterval()
45
+ | SLOW BACKGROUND JOB |
46
+ | INTERVAL | SLOW BACKGROUND JOB |
47
+ | INTERVAL | ...
48
+
49
+ backgroundJob()
50
+ | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
51
+ ```
52
+
53
+ 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
54
+
55
+ 3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (not waiting the interval).
56
+
57
+ 4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
58
+
59
+ **Example**
60
+
61
+ ```javascript
62
+ import * as utilities from "@radically-straightforward/utilities";
63
+ import * as node from "@radically-straightforward/node";
64
+
65
+ const backgroundJob = utilities.backgroundJob(
66
+ { interval: 3 * 1000 },
67
+ async () => {
68
+ console.log("backgroundJob(): Running background job...");
69
+ await utilities.sleep(3 * 1000);
70
+ console.log("backgroundJob(): ...finished running background job.");
71
+ },
72
+ );
73
+ console.log(
74
+ "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
75
+ );
76
+ process.on("SIGTSTP", () => {
77
+ backgroundJob.run();
78
+ });
79
+ await node.shouldTerminate();
80
+ backgroundJob.stop();
81
+ ```
82
+
83
+ ### `sleep()`
84
+
85
+ ```typescript
86
+ export function sleep(duration: number): Promise<void>;
87
+ ```
88
+
89
+ A promisified version of `setTimeout()`. Bare-bones: It doesn’t even offer a way to `clearTimeout()`. Useful in JavaScript that may run in the browser—if you’re only targeting Node.js then you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
90
+
91
+ ### `randomString()`
92
+
93
+ ```typescript
94
+ export function randomString(): string;
95
+ ```
96
+
97
+ A fast random string generator. The generated strings are 10 or 11 characters in length. The generated strings include the characters `[0-9a-z]`. The generated strings are **not** cryptographically secure—if you need that, then use [`crypto-random-string`](https://npm.im/crypto-random-string).
98
+
99
+ ### `log()`
100
+
101
+ ```typescript
102
+ export function log(...messageParts: string[]): void;
103
+ ```
104
+
105
+ Tab-separated logging.
106
+
19
107
  ### `Intern`
20
108
 
21
109
  ```typescript
@@ -109,7 +197,7 @@ $([1]) === $([1]); // => true
109
197
 
110
198
  > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
111
199
 
112
- > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
200
+ > **Note:** The pool of interned values is available as `intern.pool`. The interned values are kept with `WeakRef`s to allow them to be garbage collected when they aren’t referenced anywhere else anymore. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
113
201
 
114
202
  **Related Work**
115
203
 
package/build/index.d.mts CHANGED
@@ -1,3 +1,69 @@
1
+ /**
2
+ * Start a background job that runs every `interval`.
3
+ *
4
+ * This is different from `setInterval()` in the following ways:
5
+ *
6
+ * 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
7
+ *
8
+ * ```
9
+ * setInterval()
10
+ * | SLOW BACKGROUND JOB |
11
+ * | INTERVAL | SLOW BACKGROUND JOB |
12
+ * | INTERVAL | ...
13
+ *
14
+ * backgroundJob()
15
+ * | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
16
+ * ```
17
+ *
18
+ * 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
19
+ *
20
+ * 3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (not waiting the interval).
21
+ *
22
+ * 4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
23
+ *
24
+ * **Example**
25
+ *
26
+ * ```javascript
27
+ * import * as utilities from "@radically-straightforward/utilities";
28
+ * import * as node from "@radically-straightforward/node";
29
+ *
30
+ * const backgroundJob = utilities.backgroundJob(
31
+ * { interval: 3 * 1000 },
32
+ * async () => {
33
+ * console.log("backgroundJob(): Running background job...");
34
+ * await utilities.sleep(3 * 1000);
35
+ * console.log("backgroundJob(): ...finished running background job.");
36
+ * },
37
+ * );
38
+ * console.log(
39
+ * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
40
+ * );
41
+ * process.on("SIGTSTP", () => {
42
+ * backgroundJob.run();
43
+ * });
44
+ * await node.shouldTerminate();
45
+ * backgroundJob.stop();
46
+ * ```
47
+ */
48
+ export declare function backgroundJob({ interval, intervalVariance, }: {
49
+ interval: number;
50
+ intervalVariance?: number;
51
+ }, job: () => void | Promise<void>): {
52
+ run: () => void;
53
+ stop: () => void;
54
+ };
55
+ /**
56
+ * A promisified version of `setTimeout()`. Bare-bones: It doesn’t even offer a way to `clearTimeout()`. Useful in JavaScript that may run in the browser—if you’re only targeting Node.js then you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
57
+ */
58
+ export declare function sleep(duration: number): Promise<void>;
59
+ /**
60
+ * A fast random string generator. The generated strings are 10 or 11 characters in length. The generated strings include the characters `[0-9a-z]`. The generated strings are **not** cryptographically secure—if you need that, then use [`crypto-random-string`](https://npm.im/crypto-random-string).
61
+ */
62
+ export declare function randomString(): string;
63
+ /**
64
+ * Tab-separated logging.
65
+ */
66
+ export declare function log(...messageParts: string[]): void;
1
67
  /**
2
68
  * Utility type for `intern()`.
3
69
  */
@@ -62,7 +128,7 @@ export type InternInnerValue = string | number | bigint | boolean | symbol | und
62
128
  *
63
129
  * > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
64
130
  *
65
- * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
131
+ * > **Note:** The pool of interned values is available as `intern.pool`. The interned values are kept with `WeakRef`s to allow them to be garbage collected when they aren’t referenced anywhere else anymore. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
66
132
  *
67
133
  * **Related Work**
68
134
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,GAAG;IAAE,CAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,IAAI,GACJ,MAAM,CAAC,OAAO,CAAC,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGG;AACH,wBAAgB,MAAM,CACpB,CAAC,SAAS,KAAK,CAAC,gBAAgB,CAAC,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAAE,EACvE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CA2CrB;yBA7Ce,MAAM;;;;;;;;;;;;;;;;AA+CtB,eAAO,MAAM,YAAY,eAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,wBAAgB,aAAa,CAC3B,EACE,QAAQ,EACR,gBAAsB,GACvB,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,EAClD,GAAG,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAC9B;IAAE,GAAG,EAAE,MAAM,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,IAAI,CAAA;CAAE,CAuCvC;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAED;;GAEG;AACH,wBAAgB,GAAG,CAAC,GAAG,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,CAEnD;AAED;;GAEG;AACH,MAAM,MAAM,MAAM,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,GAAG;IAAE,CAAC,YAAY,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAAC;AAErE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GACxB,MAAM,GACN,MAAM,GACN,MAAM,GACN,OAAO,GACP,MAAM,GACN,SAAS,GACT,IAAI,GACJ,MAAM,CAAC,OAAO,CAAC,CAAC;AAEpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqGG;AACH,wBAAgB,MAAM,CACpB,CAAC,SAAS,KAAK,CAAC,gBAAgB,CAAC,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAAE,EACvE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CA2CrB;yBA7Ce,MAAM;;;;;;;;;;;;;;;;AA+CtB,eAAO,MAAM,YAAY,eAAmB,CAAC"}
package/build/index.mjs CHANGED
@@ -1,3 +1,101 @@
1
+ /**
2
+ * Start a background job that runs every `interval`.
3
+ *
4
+ * This is different from `setInterval()` in the following ways:
5
+ *
6
+ * 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
7
+ *
8
+ * ```
9
+ * setInterval()
10
+ * | SLOW BACKGROUND JOB |
11
+ * | INTERVAL | SLOW BACKGROUND JOB |
12
+ * | INTERVAL | ...
13
+ *
14
+ * backgroundJob()
15
+ * | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
16
+ * ```
17
+ *
18
+ * 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
19
+ *
20
+ * 3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (not waiting the interval).
21
+ *
22
+ * 4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
23
+ *
24
+ * **Example**
25
+ *
26
+ * ```javascript
27
+ * import * as utilities from "@radically-straightforward/utilities";
28
+ * import * as node from "@radically-straightforward/node";
29
+ *
30
+ * const backgroundJob = utilities.backgroundJob(
31
+ * { interval: 3 * 1000 },
32
+ * async () => {
33
+ * console.log("backgroundJob(): Running background job...");
34
+ * await utilities.sleep(3 * 1000);
35
+ * console.log("backgroundJob(): ...finished running background job.");
36
+ * },
37
+ * );
38
+ * console.log(
39
+ * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
40
+ * );
41
+ * process.on("SIGTSTP", () => {
42
+ * backgroundJob.run();
43
+ * });
44
+ * await node.shouldTerminate();
45
+ * backgroundJob.stop();
46
+ * ```
47
+ */
48
+ export function backgroundJob({ interval, intervalVariance = 0.1, }, job) {
49
+ let state = "initial";
50
+ let timeout = undefined;
51
+ async function run() {
52
+ state = "running";
53
+ await job();
54
+ if (state === "running" || state === "runningAndMarkedForRerun") {
55
+ timeout = setTimeout(run, state === "runningAndMarkedForRerun"
56
+ ? 0
57
+ : interval + interval * intervalVariance * Math.random());
58
+ state = "sleeping";
59
+ }
60
+ }
61
+ run();
62
+ return {
63
+ run: () => {
64
+ switch (state) {
65
+ case "sleeping":
66
+ clearTimeout(timeout);
67
+ run();
68
+ break;
69
+ case "running":
70
+ state = "runningAndMarkedForRerun";
71
+ break;
72
+ }
73
+ },
74
+ stop: () => {
75
+ if (state === "sleeping")
76
+ clearTimeout(timeout);
77
+ state = "stopped";
78
+ },
79
+ };
80
+ }
81
+ /**
82
+ * A promisified version of `setTimeout()`. Bare-bones: It doesn’t even offer a way to `clearTimeout()`. Useful in JavaScript that may run in the browser—if you’re only targeting Node.js then you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
83
+ */
84
+ export function sleep(duration) {
85
+ return new Promise((resolve) => setTimeout(resolve, duration));
86
+ }
87
+ /**
88
+ * A fast random string generator. The generated strings are 10 or 11 characters in length. The generated strings include the characters `[0-9a-z]`. The generated strings are **not** cryptographically secure—if you need that, then use [`crypto-random-string`](https://npm.im/crypto-random-string).
89
+ */
90
+ export function randomString() {
91
+ return Math.random().toString(36).slice(2);
92
+ }
93
+ /**
94
+ * Tab-separated logging.
95
+ */
96
+ export function log(...messageParts) {
97
+ console.log(messageParts.join(" \t"));
98
+ }
1
99
  /**
2
100
  * [Interning](<https://en.wikipedia.org/wiki/Interning_(computer_science)>) a value makes it unique across the program, which is useful for checking equality with `===` (reference equality), using it as a key in a `Map`, adding it to a `Set`, and so forth:
3
101
  *
@@ -52,7 +150,7 @@
52
150
  *
53
151
  * > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
54
152
  *
55
- * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
153
+ * > **Note:** The pool of interned values is available as `intern.pool`. The interned values are kept with `WeakRef`s to allow them to be garbage collected when they aren’t referenced anywhere else anymore. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
56
154
  *
57
155
  * **Related Work**
58
156
  *
@@ -144,94 +242,4 @@ intern.pool = {
144
242
  intern.finalizationRegistry = new FinalizationRegistry(({ type, key }) => {
145
243
  intern.pool[type].delete(key);
146
244
  });
147
- /*
148
-
149
-
150
- Math.random().toString(36).slice(2)
151
-
152
-
153
-
154
-
155
- https://npm.im/package/p-timeout
156
- https://npm.im/package/delay
157
- https://npm.im/package/sleep-promise
158
- https://npm.im/package/promise-timeout
159
- https://npm.im/package/sleep
160
- https://npm.im/package/timeout-as-promise
161
- https://npm.im/package/delayed
162
- https://npm.im/package/sleep-async
163
- https://npm.im/package/promise.timeout
164
-
165
- */
166
- // /**
167
- // - Remove uses of `node:timers/promises`?
168
- // *
169
- // * TODO: In universal JavaScript, implement a way to **canonicalize** objects using deepEquals to be used in Sets and as Map keys (then deprecate `collections-deep-equal`).
170
- // * - value objects
171
- // * - https://lodash.com/docs/4.17.15#isEqual
172
- // * TODO: Implement using setTimeout and let it be usable in client-side JavaScript as well.
173
- // * TODO: Explain the differences between this and `setInterval()` (wait for completion before setting the next scheduler, and force a job to run)
174
- // *
175
- // * Start a background job that runs every given `interval`.
176
- // *
177
- // * You may use `backgroundJob.run()` to force the background job to run right away.
178
- // *
179
- // * **Note:** If a background job is running when `backgroundJob.continue()` is called.
180
- // *
181
- // * You may use `backgroundJob.stop()` to stop the background job.
182
- // *
183
- // * **Note:** If a background job is running when `backgroundJob.stop()` is called, then that background job is run to completion, but a future background job run is not scheduled. This is similar to how an HTTP server may stop finish processing existing requests but don’t accept new requests.
184
- // *
185
- // * **Note:** The `intervalVariance` prevents many background jobs from starting at the same and overloading the machine.
186
- // *
187
- // * **Example**
188
- // *
189
- // * ```javascript
190
- // * import * as node from "@radically-straightforward/node";
191
- // *
192
- // * const backgroundJob = node.backgroundJob({ interval: 3 * 1000 }, () => {
193
- // * console.log("backgroundJob(): Running background job...");
194
- // * });
195
- // * process.on("SIGTSTP", () => {
196
- // * backgroundJob.run();
197
- // * });
198
- // * console.log(
199
- // * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
200
- // * );
201
- // * await node.shouldTerminate();
202
- // * backgroundJob.stop();
203
- // * ```
204
- // */
205
- // export function backgroundJob(
206
- // {
207
- // interval,
208
- // intervalVariance = 0.1,
209
- // }: { interval: number; intervalVariance?: number },
210
- // function_: () => void | Promise<void>,
211
- // ): { run: () => void; stop: () => void } {
212
- // let shouldContinue = true;
213
- // let abortController = new AbortController();
214
- // (async () => {
215
- // while (shouldContinue) {
216
- // await function_();
217
- // await timers
218
- // .setTimeout(
219
- // interval + interval * intervalVariance * Math.random(),
220
- // undefined,
221
- // { signal: abortController.signal },
222
- // )
223
- // .catch(() => {});
224
- // abortController = new AbortController();
225
- // }
226
- // })();
227
- // return {
228
- // run: () => {
229
- // abortController.abort();
230
- // },
231
- // stop: () => {
232
- // shouldContinue = false;
233
- // abortController.abort();
234
- // },
235
- // };
236
- // }
237
245
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../source/index.mts"],"names":[],"mappings":"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"}
@@ -1,6 +1,40 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
+ import * as node from "@radically-straightforward/node";
4
+ import * as utilities from "./index.mjs";
3
5
  import { intern as $ } from "./index.mjs";
6
+ test("backgroundJob()", {
7
+ ...(!process.stdin.isTTY
8
+ ? {
9
+ skip: "Run interactive test with ‘node ./build/index.test.mjs’.",
10
+ }
11
+ : {}),
12
+ }, async () => {
13
+ const backgroundJob = utilities.backgroundJob({ interval: 3 * 1000 }, async () => {
14
+ console.log("backgroundJob(): Running background job...");
15
+ await utilities.sleep(3 * 1000);
16
+ console.log("backgroundJob(): ...finished running background job.");
17
+ });
18
+ console.log("backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...");
19
+ process.on("SIGTSTP", () => {
20
+ backgroundJob.run();
21
+ });
22
+ await node.shouldTerminate();
23
+ backgroundJob.stop();
24
+ });
25
+ test("sleep()", async () => {
26
+ const before = Date.now();
27
+ await utilities.sleep(1000);
28
+ assert(Date.now() - before >= 1000);
29
+ });
30
+ test("randomString()", () => {
31
+ const randomString = utilities.randomString();
32
+ assert(10 <= randomString.length && randomString.length <= 11);
33
+ assert.match(randomString, /^[0-9a-z]+$/);
34
+ });
35
+ test("randomString()", () => {
36
+ utilities.log("EXAMPLE", "OF", "TAB-SEPARATED LOGGING");
37
+ });
4
38
  test("intern()", () => {
5
39
  // @ts-expect-error
6
40
  assert(([1] === [1]) === false);
@@ -45,27 +79,4 @@ test("intern()", () => {
45
79
  $([1])[0] = 2;
46
80
  });
47
81
  });
48
- // test(
49
- // "backgroundJob()",
50
- // {
51
- // ...(!process.stdin.isTTY
52
- // ? {
53
- // skip: "Run interactive test with ‘node ./build/index.test.mjs’.",
54
- // }
55
- // : {}),
56
- // },
57
- // async () => {
58
- // const backgroundJob = node.backgroundJob({ interval: 3 * 1000 }, () => {
59
- // console.log("backgroundJob(): Running background job...");
60
- // });
61
- // process.on("SIGTSTP", () => {
62
- // backgroundJob.run();
63
- // });
64
- // console.log(
65
- // "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
66
- // );
67
- // await node.shouldTerminate();
68
- // backgroundJob.stop();
69
- // },
70
- // );
71
82
  //# sourceMappingURL=index.test.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.test.mjs","sourceRoot":"","sources":["../source/index.test.mts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radically-straightforward/utilities",
3
- "version": "0.0.3",
3
+ "version": "1.0.1",
4
4
  "description": "🛠️ Utilities for Node.js and the browser",
5
5
  "keywords": [
6
6
  "node",
@@ -29,6 +29,7 @@
29
29
  },
30
30
  "devDependencies": {
31
31
  "@radically-straightforward/documentation": "^1.0.1",
32
+ "@radically-straightforward/node": "^2.0.1",
32
33
  "@radically-straightforward/tsconfig": "^1.0.0",
33
34
  "@types/node": "^20.10.6",
34
35
  "prettier": "^3.1.1",
package/source/index.mts CHANGED
@@ -1,3 +1,118 @@
1
+ /**
2
+ * Start a background job that runs every `interval`.
3
+ *
4
+ * This is different from `setInterval()` in the following ways:
5
+ *
6
+ * 1. The interval counts **between** jobs, so slow background jobs don’t get called concurrently:
7
+ *
8
+ * ```
9
+ * setInterval()
10
+ * | SLOW BACKGROUND JOB |
11
+ * | INTERVAL | SLOW BACKGROUND JOB |
12
+ * | INTERVAL | ...
13
+ *
14
+ * backgroundJob()
15
+ * | SLOW BACKGROUND JOB | INTERVAL | SLOW BACKGROUND JOB | INTERVAL | ...
16
+ * ```
17
+ *
18
+ * 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
19
+ *
20
+ * 3. You may use `backgroundJob.run()` to force the background job to run right away. If the background job is already running, calling `backgroundJob.run()` schedules it to run again as soon as possible (not waiting the interval).
21
+ *
22
+ * 4. You may use `backgroundJob.stop()` to stop the background job. If the background job is running, it will finish but it will not be scheduled to run again. This is similar to how an HTTP server may terminate gracefully by stopping accepting new requests but finishing responding to existing requests. After a job has been stopped, you may not `backgroundJob.run()` it again (calling `backgroundJob.run()` has no effect).
23
+ *
24
+ * **Example**
25
+ *
26
+ * ```javascript
27
+ * import * as utilities from "@radically-straightforward/utilities";
28
+ * import * as node from "@radically-straightforward/node";
29
+ *
30
+ * const backgroundJob = utilities.backgroundJob(
31
+ * { interval: 3 * 1000 },
32
+ * async () => {
33
+ * console.log("backgroundJob(): Running background job...");
34
+ * await utilities.sleep(3 * 1000);
35
+ * console.log("backgroundJob(): ...finished running background job.");
36
+ * },
37
+ * );
38
+ * console.log(
39
+ * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
40
+ * );
41
+ * process.on("SIGTSTP", () => {
42
+ * backgroundJob.run();
43
+ * });
44
+ * await node.shouldTerminate();
45
+ * backgroundJob.stop();
46
+ * ```
47
+ */
48
+ export function backgroundJob(
49
+ {
50
+ interval,
51
+ intervalVariance = 0.1,
52
+ }: { interval: number; intervalVariance?: number },
53
+ job: () => void | Promise<void>,
54
+ ): { run: () => void; stop: () => void } {
55
+ let state:
56
+ | "initial"
57
+ | "running"
58
+ | "runningAndMarkedForRerun"
59
+ | "sleeping"
60
+ | "stopped" = "initial";
61
+ let timeout: any = undefined;
62
+ async function run() {
63
+ state = "running";
64
+ await job();
65
+ if (state === "running" || state === "runningAndMarkedForRerun") {
66
+ timeout = setTimeout(
67
+ run,
68
+ (state as any) === "runningAndMarkedForRerun"
69
+ ? 0
70
+ : interval + interval * intervalVariance * Math.random(),
71
+ );
72
+ state = "sleeping";
73
+ }
74
+ }
75
+ run();
76
+ return {
77
+ run: () => {
78
+ switch (state) {
79
+ case "sleeping":
80
+ clearTimeout(timeout);
81
+ run();
82
+ break;
83
+ case "running":
84
+ state = "runningAndMarkedForRerun";
85
+ break;
86
+ }
87
+ },
88
+ stop: () => {
89
+ if (state === "sleeping") clearTimeout(timeout);
90
+ state = "stopped";
91
+ },
92
+ };
93
+ }
94
+
95
+ /**
96
+ * A promisified version of `setTimeout()`. Bare-bones: It doesn’t even offer a way to `clearTimeout()`. Useful in JavaScript that may run in the browser—if you’re only targeting Node.js then you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
97
+ */
98
+ export function sleep(duration: number): Promise<void> {
99
+ return new Promise((resolve) => setTimeout(resolve, duration));
100
+ }
101
+
102
+ /**
103
+ * A fast random string generator. The generated strings are 10 or 11 characters in length. The generated strings include the characters `[0-9a-z]`. The generated strings are **not** cryptographically secure—if you need that, then use [`crypto-random-string`](https://npm.im/crypto-random-string).
104
+ */
105
+ export function randomString(): string {
106
+ return Math.random().toString(36).slice(2);
107
+ }
108
+
109
+ /**
110
+ * Tab-separated logging.
111
+ */
112
+ export function log(...messageParts: string[]): void {
113
+ console.log(messageParts.join(" \t"));
114
+ }
115
+
1
116
  /**
2
117
  * Utility type for `intern()`.
3
118
  */
@@ -70,7 +185,7 @@ export type InternInnerValue =
70
185
  *
71
186
  * > **Note:** Interned objects do not preserve the order of the attributes: `$({ a: 1, b: 2 }) === $({ b: 2, a: 1 })`.
72
187
  *
73
- * > **Note:** The pool of interned values is available as `intern.pool`. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
188
+ * > **Note:** The pool of interned values is available as `intern.pool`. The interned values are kept with `WeakRef`s to allow them to be garbage collected when they aren’t referenced anywhere else anymore. There’s a `FinalizationRegistry` at `intern.finalizationRegistry` that cleans up interned values that have been garbage collected.
74
189
  *
75
190
  * **Related Work**
76
191
  *
@@ -181,94 +296,3 @@ intern.finalizationRegistry = new FinalizationRegistry<{
181
296
  }>(({ type, key }) => {
182
297
  intern.pool[type].delete(key);
183
298
  });
184
-
185
- /*
186
-
187
-
188
- Math.random().toString(36).slice(2)
189
-
190
-
191
-
192
-
193
- https://npm.im/package/p-timeout
194
- https://npm.im/package/delay
195
- https://npm.im/package/sleep-promise
196
- https://npm.im/package/promise-timeout
197
- https://npm.im/package/sleep
198
- https://npm.im/package/timeout-as-promise
199
- https://npm.im/package/delayed
200
- https://npm.im/package/sleep-async
201
- https://npm.im/package/promise.timeout
202
-
203
- */
204
- // /**
205
- // - Remove uses of `node:timers/promises`?
206
- // *
207
- // * TODO: In universal JavaScript, implement a way to **canonicalize** objects using deepEquals to be used in Sets and as Map keys (then deprecate `collections-deep-equal`).
208
- // * - value objects
209
- // * - https://lodash.com/docs/4.17.15#isEqual
210
- // * TODO: Implement using setTimeout and let it be usable in client-side JavaScript as well.
211
- // * TODO: Explain the differences between this and `setInterval()` (wait for completion before setting the next scheduler, and force a job to run)
212
- // *
213
- // * Start a background job that runs every given `interval`.
214
- // *
215
- // * You may use `backgroundJob.run()` to force the background job to run right away.
216
- // *
217
- // * **Note:** If a background job is running when `backgroundJob.continue()` is called.
218
- // *
219
- // * You may use `backgroundJob.stop()` to stop the background job.
220
- // *
221
- // * **Note:** If a background job is running when `backgroundJob.stop()` is called, then that background job is run to completion, but a future background job run is not scheduled. This is similar to how an HTTP server may stop finish processing existing requests but don’t accept new requests.
222
- // *
223
- // * **Note:** The `intervalVariance` prevents many background jobs from starting at the same and overloading the machine.
224
- // *
225
- // * **Example**
226
- // *
227
- // * ```javascript
228
- // * import * as node from "@radically-straightforward/node";
229
- // *
230
- // * const backgroundJob = node.backgroundJob({ interval: 3 * 1000 }, () => {
231
- // * console.log("backgroundJob(): Running background job...");
232
- // * });
233
- // * process.on("SIGTSTP", () => {
234
- // * backgroundJob.run();
235
- // * });
236
- // * console.log(
237
- // * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
238
- // * );
239
- // * await node.shouldTerminate();
240
- // * backgroundJob.stop();
241
- // * ```
242
- // */
243
- // export function backgroundJob(
244
- // {
245
- // interval,
246
- // intervalVariance = 0.1,
247
- // }: { interval: number; intervalVariance?: number },
248
- // function_: () => void | Promise<void>,
249
- // ): { run: () => void; stop: () => void } {
250
- // let shouldContinue = true;
251
- // let abortController = new AbortController();
252
- // (async () => {
253
- // while (shouldContinue) {
254
- // await function_();
255
- // await timers
256
- // .setTimeout(
257
- // interval + interval * intervalVariance * Math.random(),
258
- // undefined,
259
- // { signal: abortController.signal },
260
- // )
261
- // .catch(() => {});
262
- // abortController = new AbortController();
263
- // }
264
- // })();
265
- // return {
266
- // run: () => {
267
- // abortController.abort();
268
- // },
269
- // stop: () => {
270
- // shouldContinue = false;
271
- // abortController.abort();
272
- // },
273
- // };
274
- // }
@@ -1,8 +1,54 @@
1
1
  import test from "node:test";
2
2
  import assert from "node:assert/strict";
3
+ import * as node from "@radically-straightforward/node";
3
4
  import * as utilities from "./index.mjs";
4
5
  import { intern as $ } from "./index.mjs";
5
6
 
7
+ test(
8
+ "backgroundJob()",
9
+ {
10
+ ...(!process.stdin.isTTY
11
+ ? {
12
+ skip: "Run interactive test with ‘node ./build/index.test.mjs’.",
13
+ }
14
+ : {}),
15
+ },
16
+ async () => {
17
+ const backgroundJob = utilities.backgroundJob(
18
+ { interval: 3 * 1000 },
19
+ async () => {
20
+ console.log("backgroundJob(): Running background job...");
21
+ await utilities.sleep(3 * 1000);
22
+ console.log("backgroundJob(): ...finished running background job.");
23
+ },
24
+ );
25
+ console.log(
26
+ "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
27
+ );
28
+ process.on("SIGTSTP", () => {
29
+ backgroundJob.run();
30
+ });
31
+ await node.shouldTerminate();
32
+ backgroundJob.stop();
33
+ },
34
+ );
35
+
36
+ test("sleep()", async () => {
37
+ const before = Date.now();
38
+ await utilities.sleep(1000);
39
+ assert(Date.now() - before >= 1000);
40
+ });
41
+
42
+ test("randomString()", () => {
43
+ const randomString = utilities.randomString();
44
+ assert(10 <= randomString.length && randomString.length <= 11);
45
+ assert.match(randomString, /^[0-9a-z]+$/);
46
+ });
47
+
48
+ test("randomString()", () => {
49
+ utilities.log("EXAMPLE", "OF", "TAB-SEPARATED LOGGING");
50
+ });
51
+
6
52
  test("intern()", () => {
7
53
  // @ts-expect-error
8
54
  assert(([1] === [1]) === false);
@@ -54,27 +100,3 @@ test("intern()", () => {
54
100
  $([1])[0] = 2;
55
101
  });
56
102
  });
57
-
58
- // test(
59
- // "backgroundJob()",
60
- // {
61
- // ...(!process.stdin.isTTY
62
- // ? {
63
- // skip: "Run interactive test with ‘node ./build/index.test.mjs’.",
64
- // }
65
- // : {}),
66
- // },
67
- // async () => {
68
- // const backgroundJob = node.backgroundJob({ interval: 3 * 1000 }, () => {
69
- // console.log("backgroundJob(): Running background job...");
70
- // });
71
- // process.on("SIGTSTP", () => {
72
- // backgroundJob.run();
73
- // });
74
- // console.log(
75
- // "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
76
- // );
77
- // await node.shouldTerminate();
78
- // backgroundJob.stop();
79
- // },
80
- // );