@radically-straightforward/utilities 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.1.0 · 2024-02-21
4
+
5
+ - Changed `backgroundJob()` so that in Node.js it terminates gracefully.
6
+
7
+ ## 1.0.1 · 2024-01-09
8
+
9
+ - Added `log()`.
10
+
3
11
  ## 1.0.0 · 2024-01-06
4
12
 
5
13
  - Added `backgroundJob()`.
package/README.md CHANGED
@@ -52,15 +52,16 @@ This is different from `setInterval()` in the following ways:
52
52
 
53
53
  2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
54
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).
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 (with a wait interval of 0).
56
56
 
57
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
58
 
59
+ 5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
60
+
59
61
  **Example**
60
62
 
61
63
  ```javascript
62
64
  import * as utilities from "@radically-straightforward/utilities";
63
- import * as node from "@radically-straightforward/node";
64
65
 
65
66
  const backgroundJob = utilities.backgroundJob(
66
67
  { interval: 3 * 1000 },
@@ -70,14 +71,12 @@ const backgroundJob = utilities.backgroundJob(
70
71
  console.log("backgroundJob(): ...finished running background job.");
71
72
  },
72
73
  );
73
- console.log(
74
- "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
75
- );
76
74
  process.on("SIGTSTP", () => {
77
75
  backgroundJob.run();
78
76
  });
79
- await node.shouldTerminate();
80
- backgroundJob.stop();
77
+ console.log(
78
+ "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
79
+ );
81
80
  ```
82
81
 
83
82
  ### `sleep()`
@@ -86,7 +85,7 @@ backgroundJob.stop();
86
85
  export function sleep(duration: number): Promise<void>;
87
86
  ```
88
87
 
89
- A promisified version of `setTimeout()`. It doesn’t offer a way to `clearTimeout()`. Useful in the browser—in Node.js you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
88
+ 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
89
 
91
90
  ### `randomString()`
92
91
 
@@ -94,7 +93,15 @@ A promisified version of `setTimeout()`. It doesn’t offer a way to `clearTimeo
94
93
  export function randomString(): string;
95
94
  ```
96
95
 
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).
96
+ 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://www.npmjs.com/package/crypto-random-string).
97
+
98
+ ### `log()`
99
+
100
+ ```typescript
101
+ export function log(...messageParts: string[]): void;
102
+ ```
103
+
104
+ Tab-separated logging.
98
105
 
99
106
  ### `Intern`
100
107
 
@@ -128,12 +135,12 @@ Utility type for `intern()`.
128
135
 
129
136
  ```typescript
130
137
  export function intern<
131
- T extends
138
+ Type extends
132
139
  | Array<InternInnerValue>
133
140
  | {
134
141
  [key: string]: InternInnerValue;
135
142
  },
136
- >(value: T): Intern<T>;
143
+ >(value: Type): Intern<Type>;
137
144
  ```
138
145
 
139
146
  [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:
@@ -199,7 +206,7 @@ A proposal to include immutable objects (Records) and immutable arrays (Tuples)
199
206
 
200
207
  It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
201
208
 
202
- **[`collections-deep-equal`](https://npm.im/collections-deep-equal)**
209
+ **[`collections-deep-equal`](https://www.npmjs.com/package/collections-deep-equal)**
203
210
 
204
211
  A previous solution to this problem which took a different approach: Instead of interning the values and allowing you to use JavaScript’s `Map`s and `Set`s, `collections-deep-equal` extends `Map`s and `Set`s with a different notion of equality.
205
212
 
@@ -209,21 +216,21 @@ A previous solution to this problem which took a different approach: Instead of
209
216
 
210
217
  `collections-deep-equal` has different intern pools for each `Map` and `Set` instead of `intern()`’s single global intern pool, which may be advantageous because smaller pools may be faster to traverse.
211
218
 
212
- **[Immutable.js](https://npm.im/immutable), [`collections`](https://npm.im/collections), [`mori`](https://npm.im/mori), [TypeScript Collections](https://npm.im/typescript-collections), [`prelude-ts`](https://npm.im/prelude-ts), [`collectable`](https://npm.im/collectable), and so forth**
219
+ **[Immutable.js](https://www.npmjs.com/package/immutable), [`collections`](https://www.npmjs.com/package/collections), [`mori`](https://www.npmjs.com/package/mori), [TypeScript Collections](https://www.npmjs.com/package/typescript-collections), [`prelude-ts`](https://www.npmjs.com/package/prelude-ts), [`collectable`](https://www.npmjs.com/package/collectable), and so forth**
213
220
 
214
221
  Similar to `collections-deep-equal`, these libraries implement their own data structures instead of relying on JavaScript’s `Map`s and `Set`s. Some of them go a step further and add their own notions of objects and arrays, which requires you to convert your values back and forth, may not show up nicely in the JavaScript inspector, may be less ergonomic to use with TypeScript, and so forth.
215
222
 
216
223
  The advantage of these libraries over interning is that they may be faster.
217
224
 
218
- **[`immer`](https://npm.im/immer) and [`icepick`](https://npm.im/icepick)**
225
+ **[`immer`](https://www.npmjs.com/package/immer) and [`icepick`](https://www.npmjs.com/package/icepick)**
219
226
 
220
227
  Introduce a new way to create values based on existing values.
221
228
 
222
- **[`seamless-immutable`](https://npm.im/seamless-immutable)**
229
+ **[`seamless-immutable`](https://www.npmjs.com/package/seamless-immutable)**
223
230
 
224
231
  Modifies existing values more profoundly than freezing.
225
232
 
226
- **[`es6-array-map`](https://npm.im/es6-array-map), [`valuecollection`](https://npm.im/valuecollection), [`@strong-roots-capital/map-objects`](https://npm.im/@strong-roots-capital/map-objects), and so forth**
233
+ **[`es6-array-map`](https://www.npmjs.com/package/es6-array-map), [`valuecollection`](https://www.npmjs.com/package/valuecollection), [`@strong-roots-capital/map-objects`](https://www.npmjs.com/package/@strong-roots-capital/map-objects), and so forth**
227
234
 
228
235
  Similar to `collections-deep-equal` but either incomplete, or lacking type definitions, and so forth.
229
236
 
package/build/index.d.mts CHANGED
@@ -17,15 +17,16 @@
17
17
  *
18
18
  * 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
19
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).
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 (with a wait interval of 0).
21
21
  *
22
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
23
  *
24
+ * 5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
25
+ *
24
26
  * **Example**
25
27
  *
26
28
  * ```javascript
27
29
  * import * as utilities from "@radically-straightforward/utilities";
28
- * import * as node from "@radically-straightforward/node";
29
30
  *
30
31
  * const backgroundJob = utilities.backgroundJob(
31
32
  * { interval: 3 * 1000 },
@@ -35,14 +36,12 @@
35
36
  * console.log("backgroundJob(): ...finished running background job.");
36
37
  * },
37
38
  * );
38
- * console.log(
39
- * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
40
- * );
41
39
  * process.on("SIGTSTP", () => {
42
40
  * backgroundJob.run();
43
41
  * });
44
- * await node.shouldTerminate();
45
- * backgroundJob.stop();
42
+ * console.log(
43
+ * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
44
+ * );
46
45
  * ```
47
46
  */
48
47
  export declare function backgroundJob({ interval, intervalVariance, }: {
@@ -53,13 +52,17 @@ export declare function backgroundJob({ interval, intervalVariance, }: {
53
52
  stop: () => void;
54
53
  };
55
54
  /**
56
- * A promisified version of `setTimeout()`. It doesn’t offer a way to `clearTimeout()`. Useful in the browser—in Node.js you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
55
+ * 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
56
  */
58
57
  export declare function sleep(duration: number): Promise<void>;
59
58
  /**
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).
59
+ * 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://www.npmjs.com/package/crypto-random-string).
61
60
  */
62
61
  export declare function randomString(): string;
62
+ /**
63
+ * Tab-separated logging.
64
+ */
65
+ export declare function log(...messageParts: string[]): void;
63
66
  /**
64
67
  * Utility type for `intern()`.
65
68
  */
@@ -134,7 +137,7 @@ export type InternInnerValue = string | number | bigint | boolean | symbol | und
134
137
  *
135
138
  * It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
136
139
  *
137
- * **[`collections-deep-equal`](https://npm.im/collections-deep-equal)**
140
+ * **[`collections-deep-equal`](https://www.npmjs.com/package/collections-deep-equal)**
138
141
  *
139
142
  * A previous solution to this problem which took a different approach: Instead of interning the values and allowing you to use JavaScript’s `Map`s and `Set`s, `collections-deep-equal` extends `Map`s and `Set`s with a different notion of equality.
140
143
  *
@@ -144,21 +147,21 @@ export type InternInnerValue = string | number | bigint | boolean | symbol | und
144
147
  *
145
148
  * `collections-deep-equal` has different intern pools for each `Map` and `Set` instead of `intern()`’s single global intern pool, which may be advantageous because smaller pools may be faster to traverse.
146
149
  *
147
- * **[Immutable.js](https://npm.im/immutable), [`collections`](https://npm.im/collections), [`mori`](https://npm.im/mori), [TypeScript Collections](https://npm.im/typescript-collections), [`prelude-ts`](https://npm.im/prelude-ts), [`collectable`](https://npm.im/collectable), and so forth**
150
+ * **[Immutable.js](https://www.npmjs.com/package/immutable), [`collections`](https://www.npmjs.com/package/collections), [`mori`](https://www.npmjs.com/package/mori), [TypeScript Collections](https://www.npmjs.com/package/typescript-collections), [`prelude-ts`](https://www.npmjs.com/package/prelude-ts), [`collectable`](https://www.npmjs.com/package/collectable), and so forth**
148
151
  *
149
152
  * Similar to `collections-deep-equal`, these libraries implement their own data structures instead of relying on JavaScript’s `Map`s and `Set`s. Some of them go a step further and add their own notions of objects and arrays, which requires you to convert your values back and forth, may not show up nicely in the JavaScript inspector, may be less ergonomic to use with TypeScript, and so forth.
150
153
  *
151
154
  * The advantage of these libraries over interning is that they may be faster.
152
155
  *
153
- * **[`immer`](https://npm.im/immer) and [`icepick`](https://npm.im/icepick)**
156
+ * **[`immer`](https://www.npmjs.com/package/immer) and [`icepick`](https://www.npmjs.com/package/icepick)**
154
157
  *
155
158
  * Introduce a new way to create values based on existing values.
156
159
  *
157
- * **[`seamless-immutable`](https://npm.im/seamless-immutable)**
160
+ * **[`seamless-immutable`](https://www.npmjs.com/package/seamless-immutable)**
158
161
  *
159
162
  * Modifies existing values more profoundly than freezing.
160
163
  *
161
- * **[`es6-array-map`](https://npm.im/es6-array-map), [`valuecollection`](https://npm.im/valuecollection), [`@strong-roots-capital/map-objects`](https://npm.im/@strong-roots-capital/map-objects), and so forth**
164
+ * **[`es6-array-map`](https://www.npmjs.com/package/es6-array-map), [`valuecollection`](https://www.npmjs.com/package/valuecollection), [`@strong-roots-capital/map-objects`](https://www.npmjs.com/package/@strong-roots-capital/map-objects), and so forth**
162
165
  *
163
166
  * Similar to `collections-deep-equal` but either incomplete, or lacking type definitions, and so forth.
164
167
  *
@@ -172,9 +175,9 @@ export type InternInnerValue = string | number | bigint | boolean | symbol | und
172
175
  * - <https://twitter.com/swannodette/status/1067962983924539392>
173
176
  * - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
174
177
  */
175
- export declare function intern<T extends Array<InternInnerValue> | {
178
+ export declare function intern<Type extends Array<InternInnerValue> | {
176
179
  [key: string]: InternInnerValue;
177
- }>(value: T): Intern<T>;
180
+ }>(value: Type): Intern<Type>;
178
181
  export declare namespace intern {
179
182
  var pool: {
180
183
  tuple: Map<Symbol, WeakRef<Readonly<InternInnerValue[] & {
@@ -1 +1 @@
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,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":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;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,IAAI,SAAS,KAAK,CAAC,gBAAgB,CAAC,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAAA;CAAE,EAC1E,KAAK,EAAE,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAyC3B;yBA3Ce,MAAM;;;;;;;;;;;;;;;;AA6CtB,eAAO,MAAM,YAAY,eAAmB,CAAC"}
package/build/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ if (process !== undefined)
2
+ await import("@radically-straightforward/node");
1
3
  /**
2
4
  * Start a background job that runs every `interval`.
3
5
  *
@@ -17,15 +19,16 @@
17
19
  *
18
20
  * 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
19
21
  *
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).
22
+ * 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 (with a wait interval of 0).
21
23
  *
22
24
  * 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
25
  *
26
+ * 5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
27
+ *
24
28
  * **Example**
25
29
  *
26
30
  * ```javascript
27
31
  * import * as utilities from "@radically-straightforward/utilities";
28
- * import * as node from "@radically-straightforward/node";
29
32
  *
30
33
  * const backgroundJob = utilities.backgroundJob(
31
34
  * { interval: 3 * 1000 },
@@ -35,36 +38,32 @@
35
38
  * console.log("backgroundJob(): ...finished running background job.");
36
39
  * },
37
40
  * );
38
- * console.log(
39
- * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
40
- * );
41
41
  * process.on("SIGTSTP", () => {
42
42
  * backgroundJob.run();
43
43
  * });
44
- * await node.shouldTerminate();
45
- * backgroundJob.stop();
44
+ * console.log(
45
+ * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
46
+ * );
46
47
  * ```
47
48
  */
48
49
  export function backgroundJob({ interval, intervalVariance = 0.1, }, job) {
49
- let state = "initial";
50
+ let state = "sleeping";
50
51
  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: () => {
52
+ const scheduler = {
53
+ run: async () => {
64
54
  switch (state) {
65
55
  case "sleeping":
66
56
  clearTimeout(timeout);
67
- run();
57
+ state = "running";
58
+ await job();
59
+ if (state === "running" || state === "runningAndMarkedForRerun") {
60
+ timeout = setTimeout(() => {
61
+ scheduler.run();
62
+ }, state === "runningAndMarkedForRerun"
63
+ ? 0
64
+ : interval + interval * intervalVariance * Math.random());
65
+ state = "sleeping";
66
+ }
68
67
  break;
69
68
  case "running":
70
69
  state = "runningAndMarkedForRerun";
@@ -72,24 +71,35 @@ export function backgroundJob({ interval, intervalVariance = 0.1, }, job) {
72
71
  }
73
72
  },
74
73
  stop: () => {
75
- if (state === "sleeping")
76
- clearTimeout(timeout);
74
+ clearTimeout(timeout);
77
75
  state = "stopped";
78
76
  },
79
77
  };
78
+ scheduler.run();
79
+ if (process !== undefined)
80
+ process.once("gracefulTermination", () => {
81
+ scheduler.stop();
82
+ });
83
+ return scheduler;
80
84
  }
81
85
  /**
82
- * A promisified version of `setTimeout()`. It doesn’t offer a way to `clearTimeout()`. Useful in the browser—in Node.js you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
86
+ * 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
87
  */
84
88
  export function sleep(duration) {
85
89
  return new Promise((resolve) => setTimeout(resolve, duration));
86
90
  }
87
91
  /**
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).
92
+ * 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://www.npmjs.com/package/crypto-random-string).
89
93
  */
90
94
  export function randomString() {
91
95
  return Math.random().toString(36).slice(2);
92
96
  }
97
+ /**
98
+ * Tab-separated logging.
99
+ */
100
+ export function log(...messageParts) {
101
+ console.log(messageParts.join(" \t"));
102
+ }
93
103
  /**
94
104
  * [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:
95
105
  *
@@ -154,7 +164,7 @@ export function randomString() {
154
164
  *
155
165
  * It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
156
166
  *
157
- * **[`collections-deep-equal`](https://npm.im/collections-deep-equal)**
167
+ * **[`collections-deep-equal`](https://www.npmjs.com/package/collections-deep-equal)**
158
168
  *
159
169
  * A previous solution to this problem which took a different approach: Instead of interning the values and allowing you to use JavaScript’s `Map`s and `Set`s, `collections-deep-equal` extends `Map`s and `Set`s with a different notion of equality.
160
170
  *
@@ -164,21 +174,21 @@ export function randomString() {
164
174
  *
165
175
  * `collections-deep-equal` has different intern pools for each `Map` and `Set` instead of `intern()`’s single global intern pool, which may be advantageous because smaller pools may be faster to traverse.
166
176
  *
167
- * **[Immutable.js](https://npm.im/immutable), [`collections`](https://npm.im/collections), [`mori`](https://npm.im/mori), [TypeScript Collections](https://npm.im/typescript-collections), [`prelude-ts`](https://npm.im/prelude-ts), [`collectable`](https://npm.im/collectable), and so forth**
177
+ * **[Immutable.js](https://www.npmjs.com/package/immutable), [`collections`](https://www.npmjs.com/package/collections), [`mori`](https://www.npmjs.com/package/mori), [TypeScript Collections](https://www.npmjs.com/package/typescript-collections), [`prelude-ts`](https://www.npmjs.com/package/prelude-ts), [`collectable`](https://www.npmjs.com/package/collectable), and so forth**
168
178
  *
169
179
  * Similar to `collections-deep-equal`, these libraries implement their own data structures instead of relying on JavaScript’s `Map`s and `Set`s. Some of them go a step further and add their own notions of objects and arrays, which requires you to convert your values back and forth, may not show up nicely in the JavaScript inspector, may be less ergonomic to use with TypeScript, and so forth.
170
180
  *
171
181
  * The advantage of these libraries over interning is that they may be faster.
172
182
  *
173
- * **[`immer`](https://npm.im/immer) and [`icepick`](https://npm.im/icepick)**
183
+ * **[`immer`](https://www.npmjs.com/package/immer) and [`icepick`](https://www.npmjs.com/package/icepick)**
174
184
  *
175
185
  * Introduce a new way to create values based on existing values.
176
186
  *
177
- * **[`seamless-immutable`](https://npm.im/seamless-immutable)**
187
+ * **[`seamless-immutable`](https://www.npmjs.com/package/seamless-immutable)**
178
188
  *
179
189
  * Modifies existing values more profoundly than freezing.
180
190
  *
181
- * **[`es6-array-map`](https://npm.im/es6-array-map), [`valuecollection`](https://npm.im/valuecollection), [`@strong-roots-capital/map-objects`](https://npm.im/@strong-roots-capital/map-objects), and so forth**
191
+ * **[`es6-array-map`](https://www.npmjs.com/package/es6-array-map), [`valuecollection`](https://www.npmjs.com/package/valuecollection), [`@strong-roots-capital/map-objects`](https://www.npmjs.com/package/@strong-roots-capital/map-objects), and so forth**
182
192
  *
183
193
  * Similar to `collections-deep-equal` but either incomplete, or lacking type definitions, and so forth.
184
194
  *
@@ -210,14 +220,12 @@ export function intern(value) {
210
220
  return internValue;
211
221
  }
212
222
  for (const innerValue of Object.values(value))
213
- if (!([
214
- "string",
215
- "number",
216
- "bigint",
217
- "boolean",
218
- "symbol",
219
- "undefined",
220
- ].includes(typeof innerValue) ||
223
+ if (!(typeof innerValue === "string" ||
224
+ typeof innerValue === "number" ||
225
+ typeof innerValue === "bigint" ||
226
+ typeof innerValue === "boolean" ||
227
+ typeof innerValue === "symbol" ||
228
+ innerValue === undefined ||
221
229
  innerValue === null ||
222
230
  innerValue[internSymbol] === true))
223
231
  throw new Error(`Failed to intern value because of non-interned inner value.`);
@@ -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,26 +1,21 @@
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";
5
- import { intern as $ } from "./index.mjs";
3
+ import * as utilities from "@radically-straightforward/utilities";
4
+ import { intern as $ } from "@radically-straightforward/utilities";
6
5
  test("backgroundJob()", {
7
- ...(!process.stdin.isTTY
8
- ? {
9
- skip: "Run interactive test with ‘node ./build/index.test.mjs’.",
10
- }
11
- : {}),
6
+ skip: process.stdin.isTTY
7
+ ? false
8
+ : "Run interactive test with ‘node ./build/index.test.mjs’.",
12
9
  }, async () => {
13
10
  const backgroundJob = utilities.backgroundJob({ interval: 3 * 1000 }, async () => {
14
11
  console.log("backgroundJob(): Running background job...");
15
12
  await utilities.sleep(3 * 1000);
16
13
  console.log("backgroundJob(): ...finished running background job.");
17
14
  });
18
- console.log("backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...");
19
15
  process.on("SIGTSTP", () => {
20
16
  backgroundJob.run();
21
17
  });
22
- await node.shouldTerminate();
23
- backgroundJob.stop();
18
+ console.log("backgroundJob(): Press ⌃Z to force background job to run and ⌃C to gracefully terminate...");
24
19
  });
25
20
  test("sleep()", async () => {
26
21
  const before = Date.now();
@@ -32,6 +27,9 @@ test("randomString()", () => {
32
27
  assert(10 <= randomString.length && randomString.length <= 11);
33
28
  assert.match(randomString, /^[0-9a-z]+$/);
34
29
  });
30
+ test("randomString()", () => {
31
+ utilities.log("EXAMPLE", "OF", "TAB-SEPARATED LOGGING");
32
+ });
35
33
  test("intern()", () => {
36
34
  // @ts-expect-error
37
35
  assert(([1] === [1]) === false);
@@ -75,5 +73,20 @@ test("intern()", () => {
75
73
  // @ts-expect-error
76
74
  $([1])[0] = 2;
77
75
  });
76
+ {
77
+ const iterations = 1000;
78
+ console.time("intern()");
79
+ const objects = [];
80
+ for (let iteration = 0; iteration < iterations; iteration++) {
81
+ const entries = [];
82
+ for (let key = 0; key < Math.floor(Math.random() * 15); key++) {
83
+ entries.push([String(key + Math.floor(Math.random() * 15)), true]);
84
+ }
85
+ objects.push($(Object.fromEntries(entries)));
86
+ objects.push($(entries.flat()));
87
+ }
88
+ // console.log($.pool.record.size);
89
+ console.timeEnd("intern()");
90
+ }
78
91
  });
79
92
  //# 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": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "🛠️ Utilities for Node.js and the browser",
5
5
  "keywords": [
6
6
  "node",
@@ -25,11 +25,13 @@
25
25
  "types": "./build/index.d.mts",
26
26
  "scripts": {
27
27
  "prepare": "tsc && documentation",
28
- "test": "npm run prepare && node --test && prettier --check \"./README.md\" --check \"./package.json\" --check \"./tsconfig.json\" --check \"./source/**/*.mts\""
28
+ "test": "npm run prepare && node --test && prettier --check \"./README.md\" --check \"./CHANGELOG.md\" --check \"./package.json\" --check \"./tsconfig.json\" --check \"./source/**/*.mts\""
29
+ },
30
+ "dependencies": {
31
+ "@radically-straightforward/node": "^3.0.0"
29
32
  },
30
33
  "devDependencies": {
31
34
  "@radically-straightforward/documentation": "^1.0.1",
32
- "@radically-straightforward/node": "^2.0.1",
33
35
  "@radically-straightforward/tsconfig": "^1.0.0",
34
36
  "@types/node": "^20.10.6",
35
37
  "prettier": "^3.1.1",
package/source/index.mts CHANGED
@@ -1,3 +1,5 @@
1
+ if (process !== undefined) await import("@radically-straightforward/node");
2
+
1
3
  /**
2
4
  * Start a background job that runs every `interval`.
3
5
  *
@@ -17,15 +19,16 @@
17
19
  *
18
20
  * 2. We introduce a random `intervalVariance` to avoid many background jobs from starting at the same time and overloading the machine.
19
21
  *
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).
22
+ * 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 (with a wait interval of 0).
21
23
  *
22
24
  * 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
25
  *
26
+ * 5. In Node.js the background job is stopped on [`"gracefulTermination"`](https://github.com/radically-straightforward/radically-straightforward/tree/main/node#graceful-termination).
27
+ *
24
28
  * **Example**
25
29
  *
26
30
  * ```javascript
27
31
  * import * as utilities from "@radically-straightforward/utilities";
28
- * import * as node from "@radically-straightforward/node";
29
32
  *
30
33
  * const backgroundJob = utilities.backgroundJob(
31
34
  * { interval: 3 * 1000 },
@@ -35,14 +38,12 @@
35
38
  * console.log("backgroundJob(): ...finished running background job.");
36
39
  * },
37
40
  * );
38
- * console.log(
39
- * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
40
- * );
41
41
  * process.on("SIGTSTP", () => {
42
42
  * backgroundJob.run();
43
43
  * });
44
- * await node.shouldTerminate();
45
- * backgroundJob.stop();
44
+ * console.log(
45
+ * "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
46
+ * );
46
47
  * ```
47
48
  */
48
49
  export function backgroundJob(
@@ -52,33 +53,27 @@ export function backgroundJob(
52
53
  }: { interval: number; intervalVariance?: number },
53
54
  job: () => void | Promise<void>,
54
55
  ): { run: () => void; stop: () => void } {
55
- let state:
56
- | "initial"
57
- | "running"
58
- | "runningAndMarkedForRerun"
59
- | "sleeping"
60
- | "stopped" = "initial";
56
+ let state: "sleeping" | "running" | "runningAndMarkedForRerun" | "stopped" =
57
+ "sleeping";
61
58
  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: () => {
59
+ const scheduler = {
60
+ run: async () => {
78
61
  switch (state) {
79
62
  case "sleeping":
80
63
  clearTimeout(timeout);
81
- run();
64
+ state = "running";
65
+ await job();
66
+ if (state === "running" || state === "runningAndMarkedForRerun") {
67
+ timeout = setTimeout(
68
+ () => {
69
+ scheduler.run();
70
+ },
71
+ (state as any) === "runningAndMarkedForRerun"
72
+ ? 0
73
+ : interval + interval * intervalVariance * Math.random(),
74
+ );
75
+ state = "sleeping";
76
+ }
82
77
  break;
83
78
  case "running":
84
79
  state = "runningAndMarkedForRerun";
@@ -86,26 +81,39 @@ export function backgroundJob(
86
81
  }
87
82
  },
88
83
  stop: () => {
89
- if (state === "sleeping") clearTimeout(timeout);
84
+ clearTimeout(timeout);
90
85
  state = "stopped";
91
86
  },
92
87
  };
88
+ scheduler.run();
89
+ if (process !== undefined)
90
+ process.once("gracefulTermination", () => {
91
+ scheduler.stop();
92
+ });
93
+ return scheduler;
93
94
  }
94
95
 
95
96
  /**
96
- * A promisified version of `setTimeout()`. It doesn’t offer a way to `clearTimeout()`. Useful in the browser—in Node.js you’re better served by [`timersPromises.setTimeout()`](https://nodejs.org/dist/latest-v21.x/docs/api/timers.html#timerspromisessettimeoutdelay-value-options).
97
+ * 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
  */
98
99
  export function sleep(duration: number): Promise<void> {
99
100
  return new Promise((resolve) => setTimeout(resolve, duration));
100
101
  }
101
102
 
102
103
  /**
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
+ * 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://www.npmjs.com/package/crypto-random-string).
104
105
  */
105
106
  export function randomString(): string {
106
107
  return Math.random().toString(36).slice(2);
107
108
  }
108
109
 
110
+ /**
111
+ * Tab-separated logging.
112
+ */
113
+ export function log(...messageParts: string[]): void {
114
+ console.log(messageParts.join(" \t"));
115
+ }
116
+
109
117
  /**
110
118
  * Utility type for `intern()`.
111
119
  */
@@ -188,7 +196,7 @@ export type InternInnerValue =
188
196
  *
189
197
  * It includes a [polyfill](https://github.com/bloomberg/record-tuple-polyfill) which works very similarly to `intern()` but requires different functions for different data types.
190
198
  *
191
- * **[`collections-deep-equal`](https://npm.im/collections-deep-equal)**
199
+ * **[`collections-deep-equal`](https://www.npmjs.com/package/collections-deep-equal)**
192
200
  *
193
201
  * A previous solution to this problem which took a different approach: Instead of interning the values and allowing you to use JavaScript’s `Map`s and `Set`s, `collections-deep-equal` extends `Map`s and `Set`s with a different notion of equality.
194
202
  *
@@ -198,21 +206,21 @@ export type InternInnerValue =
198
206
  *
199
207
  * `collections-deep-equal` has different intern pools for each `Map` and `Set` instead of `intern()`’s single global intern pool, which may be advantageous because smaller pools may be faster to traverse.
200
208
  *
201
- * **[Immutable.js](https://npm.im/immutable), [`collections`](https://npm.im/collections), [`mori`](https://npm.im/mori), [TypeScript Collections](https://npm.im/typescript-collections), [`prelude-ts`](https://npm.im/prelude-ts), [`collectable`](https://npm.im/collectable), and so forth**
209
+ * **[Immutable.js](https://www.npmjs.com/package/immutable), [`collections`](https://www.npmjs.com/package/collections), [`mori`](https://www.npmjs.com/package/mori), [TypeScript Collections](https://www.npmjs.com/package/typescript-collections), [`prelude-ts`](https://www.npmjs.com/package/prelude-ts), [`collectable`](https://www.npmjs.com/package/collectable), and so forth**
202
210
  *
203
211
  * Similar to `collections-deep-equal`, these libraries implement their own data structures instead of relying on JavaScript’s `Map`s and `Set`s. Some of them go a step further and add their own notions of objects and arrays, which requires you to convert your values back and forth, may not show up nicely in the JavaScript inspector, may be less ergonomic to use with TypeScript, and so forth.
204
212
  *
205
213
  * The advantage of these libraries over interning is that they may be faster.
206
214
  *
207
- * **[`immer`](https://npm.im/immer) and [`icepick`](https://npm.im/icepick)**
215
+ * **[`immer`](https://www.npmjs.com/package/immer) and [`icepick`](https://www.npmjs.com/package/icepick)**
208
216
  *
209
217
  * Introduce a new way to create values based on existing values.
210
218
  *
211
- * **[`seamless-immutable`](https://npm.im/seamless-immutable)**
219
+ * **[`seamless-immutable`](https://www.npmjs.com/package/seamless-immutable)**
212
220
  *
213
221
  * Modifies existing values more profoundly than freezing.
214
222
  *
215
- * **[`es6-array-map`](https://npm.im/es6-array-map), [`valuecollection`](https://npm.im/valuecollection), [`@strong-roots-capital/map-objects`](https://npm.im/@strong-roots-capital/map-objects), and so forth**
223
+ * **[`es6-array-map`](https://www.npmjs.com/package/es6-array-map), [`valuecollection`](https://www.npmjs.com/package/valuecollection), [`@strong-roots-capital/map-objects`](https://www.npmjs.com/package/@strong-roots-capital/map-objects), and so forth**
216
224
  *
217
225
  * Similar to `collections-deep-equal` but either incomplete, or lacking type definitions, and so forth.
218
226
  *
@@ -227,8 +235,8 @@ export type InternInnerValue =
227
235
  * - <https://gist.github.com/modernserf/c000e62d40f678cf395e3f360b9b0e43>
228
236
  */
229
237
  export function intern<
230
- T extends Array<InternInnerValue> | { [key: string]: InternInnerValue },
231
- >(value: T): Intern<T> {
238
+ Type extends Array<InternInnerValue> | { [key: string]: InternInnerValue },
239
+ >(value: Type): Intern<Type> {
232
240
  const type = Array.isArray(value)
233
241
  ? "tuple"
234
242
  : typeof value === "object" && value !== null
@@ -250,14 +258,12 @@ export function intern<
250
258
  for (const innerValue of Object.values(value))
251
259
  if (
252
260
  !(
253
- [
254
- "string",
255
- "number",
256
- "bigint",
257
- "boolean",
258
- "symbol",
259
- "undefined",
260
- ].includes(typeof innerValue) ||
261
+ typeof innerValue === "string" ||
262
+ typeof innerValue === "number" ||
263
+ typeof innerValue === "bigint" ||
264
+ typeof innerValue === "boolean" ||
265
+ typeof innerValue === "symbol" ||
266
+ innerValue === undefined ||
261
267
  innerValue === null ||
262
268
  (innerValue as any)[internSymbol] === true
263
269
  )
@@ -1,17 +1,14 @@
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";
5
- import { intern as $ } from "./index.mjs";
3
+ import * as utilities from "@radically-straightforward/utilities";
4
+ import { intern as $ } from "@radically-straightforward/utilities";
6
5
 
7
6
  test(
8
7
  "backgroundJob()",
9
8
  {
10
- ...(!process.stdin.isTTY
11
- ? {
12
- skip: "Run interactive test with ‘node ./build/index.test.mjs’.",
13
- }
14
- : {}),
9
+ skip: process.stdin.isTTY
10
+ ? false
11
+ : "Run interactive test with ‘node ./build/index.test.mjs’.",
15
12
  },
16
13
  async () => {
17
14
  const backgroundJob = utilities.backgroundJob(
@@ -22,14 +19,12 @@ test(
22
19
  console.log("backgroundJob(): ...finished running background job.");
23
20
  },
24
21
  );
25
- console.log(
26
- "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to continue...",
27
- );
28
22
  process.on("SIGTSTP", () => {
29
23
  backgroundJob.run();
30
24
  });
31
- await node.shouldTerminate();
32
- backgroundJob.stop();
25
+ console.log(
26
+ "backgroundJob(): Press ⌃Z to force background job to run and ⌃C to gracefully terminate...",
27
+ );
33
28
  },
34
29
  );
35
30
 
@@ -45,6 +40,10 @@ test("randomString()", () => {
45
40
  assert.match(randomString, /^[0-9a-z]+$/);
46
41
  });
47
42
 
43
+ test("randomString()", () => {
44
+ utilities.log("EXAMPLE", "OF", "TAB-SEPARATED LOGGING");
45
+ });
46
+
48
47
  test("intern()", () => {
49
48
  // @ts-expect-error
50
49
  assert(([1] === [1]) === false);
@@ -95,4 +94,20 @@ test("intern()", () => {
95
94
  // @ts-expect-error
96
95
  $([1])[0] = 2;
97
96
  });
97
+
98
+ {
99
+ const iterations = 1000;
100
+ console.time("intern()");
101
+ const objects = [];
102
+ for (let iteration = 0; iteration < iterations; iteration++) {
103
+ const entries = [];
104
+ for (let key = 0; key < Math.floor(Math.random() * 15); key++) {
105
+ entries.push([String(key + Math.floor(Math.random() * 15)), true]);
106
+ }
107
+ objects.push($(Object.fromEntries(entries)));
108
+ objects.push($(entries.flat()));
109
+ }
110
+ // console.log($.pool.record.size);
111
+ console.timeEnd("intern()");
112
+ }
98
113
  });