@brianbuie/node-kit 0.10.0 → 0.11.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Node Kit
4
4
 
5
- Basic tools for quick node.js projects
5
+ Basic tools for Node.js projects
6
6
 
7
7
  # Installing
8
8
 
@@ -18,7 +18,7 @@ import { Fetcher, Log } from '@brianbuie/node-kit';
18
18
 
19
19
  <!--#region ts2md-api-merged-here-->
20
20
 
21
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
21
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
22
22
 
23
23
  # Classes
24
24
 
@@ -32,11 +32,12 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
32
32
  | [FileTypeCsv](#class-filetypecsv) |
33
33
  | [FileTypeJson](#class-filetypejson) |
34
34
  | [FileTypeNdjson](#class-filetypendjson) |
35
+ | [Format](#class-format) |
35
36
  | [Log](#class-log) |
36
37
  | [TempDir](#class-tempdir) |
37
38
  | [TypeWriter](#class-typewriter) |
38
39
 
39
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
40
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
40
41
 
41
42
  ---
42
43
 
@@ -59,24 +60,26 @@ export class Cache<T> {
59
60
  }
60
61
  ```
61
62
 
62
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
63
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
63
64
 
64
65
  ---
65
66
  ## Class: Dir
66
67
 
67
- Reference to a specific directory with helpful methods for resolving filepaths,
68
- sanitizing filenames, and saving files.
68
+ Reference to a specific directory with methods to create and list files.
69
+ Created immediately if it doesn't exist.
70
+ Default path: './'
69
71
 
70
72
  ```ts
71
73
  export class Dir {
72
74
  path;
73
- constructor(_path = "./")
74
- create()
75
+ constructor(inputPath = "./")
76
+ notAbsolute(subPath: string)
75
77
  dir(subPath: string)
76
78
  tempDir(subPath: string)
77
- sanitize(name: string)
79
+ sanitize(filename: string)
78
80
  filepath(base: string)
79
81
  file(base: string)
82
+ get files()
80
83
  }
81
84
  ```
82
85
 
@@ -87,7 +90,7 @@ export class Dir {
87
90
  ### Constructor
88
91
 
89
92
  ```ts
90
- constructor(_path = "./")
93
+ constructor(inputPath = "./")
91
94
  ```
92
95
 
93
96
  Argument Details
@@ -106,15 +109,15 @@ dir(subPath: string)
106
109
  Argument Details
107
110
 
108
111
  + **subPath**
109
- + to create in current Dir
112
+ + relative path to create
110
113
 
111
114
  Example
112
115
 
113
116
  ```ts
114
117
  const folder = new Dir('example');
115
- // folder.path = './example'
118
+ // folder.path = '/absolute/path/to/example'
116
119
  const child = folder.dir('path/to/dir');
117
- // child.path = './example/path/to/dir'
120
+ // child.path = '/absolute/path/to/example/path/to/dir'
118
121
  ```
119
122
 
120
123
  ### Method filepath
@@ -126,7 +129,7 @@ filepath(base: string)
126
129
  Argument Details
127
130
 
128
131
  + **base**
129
- + The file name with extension
132
+ + The file base (name and extension)
130
133
 
131
134
  Example
132
135
 
@@ -138,7 +141,7 @@ const filepath = folder.resolve('file.json');
138
141
 
139
142
  </details>
140
143
 
141
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
144
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
142
145
 
143
146
  ---
144
147
  ## Class: Fetcher
@@ -236,12 +239,12 @@ See also: [FetchOptions](#type-fetchoptions), [Route](#type-route)
236
239
 
237
240
  </details>
238
241
 
239
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
242
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
240
243
 
241
244
  ---
242
245
  ## Class: File
243
246
 
244
- WARNING: API will change!
247
+ > ⚠️ WARNING: API will change!
245
248
 
246
249
  ```ts
247
250
  export class File {
@@ -297,7 +300,7 @@ lines as strings, removes trailing '\n'
297
300
 
298
301
  </details>
299
302
 
300
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
303
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
301
304
 
302
305
  ---
303
306
  ## Class: FileType
@@ -314,7 +317,7 @@ export class FileType {
314
317
  }
315
318
  ```
316
319
 
317
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
320
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
318
321
 
319
322
  ---
320
323
  ## Class: FileTypeCsv
@@ -333,7 +336,7 @@ export class FileTypeCsv<Row extends object> extends FileType {
333
336
 
334
337
  See also: [FileType](#class-filetype)
335
338
 
336
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
339
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
337
340
 
338
341
  ---
339
342
  ## Class: FileTypeJson
@@ -351,7 +354,7 @@ export class FileTypeJson<T> extends FileType {
351
354
 
352
355
  See also: [FileType](#class-filetype)
353
356
 
354
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
357
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
355
358
 
356
359
  ---
357
360
  ## Class: FileTypeNdjson
@@ -368,7 +371,58 @@ export class FileTypeNdjson<T extends object> extends FileType {
368
371
 
369
372
  See also: [FileType](#class-filetype)
370
373
 
371
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
374
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
375
+
376
+ ---
377
+ ## Class: Format
378
+
379
+ Helpers for formatting dates, times, and numbers as strings
380
+
381
+ ```ts
382
+ export class Format {
383
+ static date(formatStr: "iso" | "ymd" | string = "iso", d: DateArg<Date> = new Date())
384
+ static round(n: number, places = 0)
385
+ static ms(ms: number)
386
+ static bytes(b: number)
387
+ }
388
+ ```
389
+
390
+ <details>
391
+
392
+ <summary>Class Format Details</summary>
393
+
394
+ ### Method date
395
+
396
+ date-fns format() with some shortcuts
397
+
398
+ ```ts
399
+ static date(formatStr: "iso" | "ymd" | string = "iso", d: DateArg<Date> = new Date())
400
+ ```
401
+
402
+ Argument Details
403
+
404
+ + **formatStr**
405
+ + 'iso' to get ISO date, 'ymd' to format as 'yyyy-MM-dd', full options: https://date-fns.org/v4.1.0/docs/format
406
+
407
+ ### Method ms
408
+
409
+ Make millisecond durations actually readable (eg "123ms", "3.56s", "1m 34s", "3h 24m", "2d 4h")
410
+
411
+ ```ts
412
+ static ms(ms: number)
413
+ ```
414
+
415
+ ### Method round
416
+
417
+ Round a number to a specific set of places
418
+
419
+ ```ts
420
+ static round(n: number, places = 0)
421
+ ```
422
+
423
+ </details>
424
+
425
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
372
426
 
373
427
  ---
374
428
  ## Class: Log
@@ -422,7 +476,6 @@ static error(...input: unknown[])
422
476
  ### Method prepare
423
477
 
424
478
  Handle first argument being a string or an object with a 'message' prop
425
- Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
426
479
 
427
480
  ```ts
428
481
  static prepare(...input: unknown[]): {
@@ -433,22 +486,38 @@ static prepare(...input: unknown[]): {
433
486
 
434
487
  </details>
435
488
 
436
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
489
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
437
490
 
438
491
  ---
439
492
  ## Class: TempDir
440
493
 
441
- Extends Dir class with method to `clear()` contents
494
+ Extends Dir class with method to `clear()` contents.
495
+ Default path: `./.${Date.now()}`
442
496
 
443
497
  ```ts
444
498
  export class TempDir extends Dir {
499
+ constructor(inputPath = `./.${Date.now()}`)
445
500
  clear()
446
501
  }
447
502
  ```
448
503
 
449
504
  See also: [Dir](#class-dir)
450
505
 
451
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
506
+ <details>
507
+
508
+ <summary>Class TempDir Details</summary>
509
+
510
+ ### Method clear
511
+
512
+ > ⚠️ Warning! This deletes the directory, make sure it's not
513
+
514
+ ```ts
515
+ clear()
516
+ ```
517
+
518
+ </details>
519
+
520
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
452
521
 
453
522
  ---
454
523
  ## Class: TypeWriter
@@ -482,7 +551,7 @@ async toString()
482
551
 
483
552
  </details>
484
553
 
485
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
554
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
486
555
 
487
556
  ---
488
557
  # Functions
@@ -490,9 +559,10 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
490
559
  | |
491
560
  | --- |
492
561
  | [snapshot](#function-snapshot) |
562
+ | [temp](#function-temp) |
493
563
  | [timeout](#function-timeout) |
494
564
 
495
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
565
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
496
566
 
497
567
  ---
498
568
 
@@ -505,7 +575,18 @@ functions are removed
505
575
  export function snapshot(i: unknown, max = 50, depth = 0): any
506
576
  ```
507
577
 
508
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
578
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
579
+
580
+ ---
581
+ ## Function: temp
582
+
583
+ Creates a '.temp' directory in current working directory
584
+
585
+ ```ts
586
+ export function temp()
587
+ ```
588
+
589
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
509
590
 
510
591
  ---
511
592
  ## Function: timeout
@@ -514,7 +595,7 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
514
595
  export async function timeout(ms: number)
515
596
  ```
516
597
 
517
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
598
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
518
599
 
519
600
  ---
520
601
  # Types
@@ -525,7 +606,7 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
525
606
  | [Query](#type-query) |
526
607
  | [Route](#type-route) |
527
608
 
528
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
609
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
529
610
 
530
611
  ---
531
612
 
@@ -545,7 +626,7 @@ export type FetchOptions = RequestInit & {
545
626
 
546
627
  See also: [Query](#type-query), [timeout](#function-timeout)
547
628
 
548
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
629
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
549
630
 
550
631
  ---
551
632
  ## Type: Query
@@ -554,7 +635,7 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
554
635
  export type Query = Record<string, QueryVal | QueryVal[]>
555
636
  ```
556
637
 
557
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
638
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
558
639
 
559
640
  ---
560
641
  ## Type: Route
@@ -563,21 +644,8 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
563
644
  export type Route = string | URL
564
645
  ```
565
646
 
566
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
567
-
568
- ---
569
- # Variables
570
-
571
- ## Variable: temp
572
-
573
- ```ts
574
- temp = new TempDir(".temp")
575
- ```
576
-
577
- See also: [TempDir](#class-tempdir)
578
-
579
- Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
647
+ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types)
580
648
 
581
649
  ---
582
650
 
583
- <!--#endregion ts2md-api-merged-here-->
651
+ <!--#endregion ts2md-api-merged-here-->
package/dist/index.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import { Readable } from "node:stream";
3
- import { Duration } from "date-fns";
3
+ import { DateArg, Duration } from "date-fns";
4
4
  import { ChalkInstance } from "chalk";
5
5
  import * as qt from "quicktype-core";
6
6
  import * as quicktype_core_dist_TargetLanguage_js0 from "quicktype-core/dist/TargetLanguage.js";
@@ -15,7 +15,7 @@ import * as quicktype_core_dist_language_Smithy4s_language_js0 from "quicktype-c
15
15
 
16
16
  //#region src/File.d.ts
17
17
  /**
18
- * WARNING: API will change!
18
+ * > ⚠️ WARNING: API will change!
19
19
  */
20
20
  declare class File {
21
21
  path: string;
@@ -90,30 +90,31 @@ declare class FileTypeCsv<Row extends object> extends FileType {
90
90
  //#endregion
91
91
  //#region src/Dir.d.ts
92
92
  /**
93
- * Reference to a specific directory with helpful methods for resolving filepaths,
94
- * sanitizing filenames, and saving files.
93
+ * Reference to a specific directory with methods to create and list files.
94
+ * Created immediately if it doesn't exist.
95
+ * Default path: './'
95
96
  */
96
97
  declare class Dir {
97
98
  path: string;
98
99
  /**
99
100
  * @param path can be relative to workspace or absolute
100
101
  */
101
- constructor(_path?: string);
102
- create(): void;
102
+ constructor(inputPath?: string);
103
+ notAbsolute(subPath: string): string;
103
104
  /**
104
105
  * Create a new Dir inside the current Dir
105
- * @param subPath to create in current Dir
106
+ * @param subPath relative path to create
106
107
  * @example
107
108
  * const folder = new Dir('example');
108
- * // folder.path = './example'
109
+ * // folder.path = '/absolute/path/to/example'
109
110
  * const child = folder.dir('path/to/dir');
110
- * // child.path = './example/path/to/dir'
111
+ * // child.path = '/absolute/path/to/example/path/to/dir'
111
112
  */
112
113
  dir(subPath: string): Dir;
113
114
  tempDir(subPath: string): TempDir;
114
- sanitize(name: string): string;
115
+ sanitize(filename: string): string;
115
116
  /**
116
- * @param base - The file name with extension
117
+ * @param base - The file base (name and extension)
117
118
  * @example
118
119
  * const folder = new Dir('example');
119
120
  * const filepath = folder.resolve('file.json');
@@ -121,17 +122,23 @@ declare class Dir {
121
122
  */
122
123
  filepath(base: string): string;
123
124
  file(base: string): File;
125
+ get files(): File[];
124
126
  }
125
127
  /**
126
- * Extends Dir class with method to `clear()` contents
128
+ * Extends Dir class with method to `clear()` contents.
129
+ * Default path: `./.${Date.now()}`
127
130
  */
128
131
  declare class TempDir extends Dir {
132
+ constructor(inputPath?: string);
133
+ /**
134
+ * > ⚠️ Warning! This deletes the directory, make sure it's not
135
+ */
129
136
  clear(): void;
130
137
  }
131
138
  /**
132
- * Common temp dir location
139
+ * Creates a '.temp' directory in current working directory
133
140
  */
134
- declare const temp: TempDir;
141
+ declare function temp(): TempDir;
135
142
  //#endregion
136
143
  //#region src/Cache.d.ts
137
144
  /**
@@ -216,6 +223,28 @@ declare class Fetcher {
216
223
  fetchJson<T>(route: Route, opts?: FetchOptions): Promise<[T, Response, Request]>;
217
224
  }
218
225
  //#endregion
226
+ //#region src/Format.d.ts
227
+ /**
228
+ * Helpers for formatting dates, times, and numbers as strings
229
+ */
230
+ declare class Format {
231
+ /**
232
+ * date-fns format() with some shortcuts
233
+ * @param formatStr
234
+ * 'iso' to get ISO date, 'ymd' to format as 'yyyy-MM-dd', full options: https://date-fns.org/v4.1.0/docs/format
235
+ */
236
+ static date(formatStr?: 'iso' | 'ymd' | string, d?: DateArg<Date>): string;
237
+ /**
238
+ * Round a number to a specific set of places
239
+ */
240
+ static round(n: number, places?: number): string;
241
+ /**
242
+ * Make millisecond durations actually readable (eg "123ms", "3.56s", "1m 34s", "3h 24m", "2d 4h")
243
+ */
244
+ static ms(ms: number): string;
245
+ static bytes(b: number): string;
246
+ }
247
+ //#endregion
219
248
  //#region src/Log.d.ts
220
249
  type Severity = 'DEFAULT' | 'DEBUG' | 'INFO' | 'NOTICE' | 'WARNING' | 'ERROR' | 'CRITICAL' | 'ALERT' | 'EMERGENCY';
221
250
  type Options = {
@@ -227,7 +256,6 @@ declare class Log {
227
256
  static isTest: boolean;
228
257
  /**
229
258
  * Handle first argument being a string or an object with a 'message' prop
230
- * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
231
259
  */
232
260
  static prepare(...input: unknown[]): {
233
261
  message?: string;
@@ -722,5 +750,5 @@ declare class TypeWriter {
722
750
  toFile(): Promise<void>;
723
751
  }
724
752
  //#endregion
725
- export { Cache, Dir, type FetchOptions, Fetcher, File, FileType, FileTypeCsv, FileTypeJson, FileTypeNdjson, Log, type Query, type Route, TempDir, TypeWriter, snapshot, temp, timeout };
753
+ export { Cache, Dir, type FetchOptions, Fetcher, File, FileType, FileTypeCsv, FileTypeJson, FileTypeNdjson, Format, Log, type Query, type Route, TempDir, TypeWriter, snapshot, temp, timeout };
726
754
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/File.ts","../src/Dir.ts","../src/Cache.ts","../src/Fetcher.ts","../src/Log.ts","../src/snapshot.ts","../src/timeout.ts","../src/TypeWriter.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/File.ts","../src/Dir.ts","../src/Cache.ts","../src/Fetcher.ts","../src/Format.ts","../src/Log.ts","../src/snapshot.ts","../src/timeout.ts","../src/TypeWriter.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/index.mjs CHANGED
@@ -5,7 +5,7 @@ import { Readable } from "node:stream";
5
5
  import { finished } from "node:stream/promises";
6
6
  import { parseStream, writeToStream } from "fast-csv";
7
7
  import { isObjectLike, merge } from "lodash-es";
8
- import { add, isAfter } from "date-fns";
8
+ import { add, format, formatISO, isAfter } from "date-fns";
9
9
  import extractDomain from "extract-domain";
10
10
  import { inspect } from "node:util";
11
11
  import chalk from "chalk";
@@ -40,7 +40,7 @@ function snapshot(i, max = 50, depth = 0) {
40
40
  //#endregion
41
41
  //#region src/File.ts
42
42
  /**
43
- * WARNING: API will change!
43
+ * > ⚠️ WARNING: API will change!
44
44
  */
45
45
  var File = class {
46
46
  path;
@@ -211,40 +211,43 @@ var FileTypeCsv = class extends FileType {
211
211
  //#endregion
212
212
  //#region src/Dir.ts
213
213
  /**
214
- * Reference to a specific directory with helpful methods for resolving filepaths,
215
- * sanitizing filenames, and saving files.
214
+ * Reference to a specific directory with methods to create and list files.
215
+ * Created immediately if it doesn't exist.
216
+ * Default path: './'
216
217
  */
217
218
  var Dir = class Dir {
218
219
  path;
219
220
  /**
220
221
  * @param path can be relative to workspace or absolute
221
222
  */
222
- constructor(_path = "./") {
223
- this.path = path.resolve(_path);
224
- }
225
- create() {
223
+ constructor(inputPath = "./") {
224
+ this.path = path.resolve(inputPath);
226
225
  fs.mkdirSync(this.path, { recursive: true });
227
226
  }
227
+ notAbsolute(subPath) {
228
+ if (path.isAbsolute(subPath)) throw new Error(`Absolute path provided: "${subPath}"`);
229
+ return subPath;
230
+ }
228
231
  /**
229
232
  * Create a new Dir inside the current Dir
230
- * @param subPath to create in current Dir
233
+ * @param subPath relative path to create
231
234
  * @example
232
235
  * const folder = new Dir('example');
233
- * // folder.path = './example'
236
+ * // folder.path = '/absolute/path/to/example'
234
237
  * const child = folder.dir('path/to/dir');
235
- * // child.path = './example/path/to/dir'
238
+ * // child.path = '/absolute/path/to/example/path/to/dir'
236
239
  */
237
240
  dir(subPath) {
238
- return new Dir(path.resolve(this.path, subPath));
241
+ return new Dir(path.resolve(this.path, this.notAbsolute(subPath)));
239
242
  }
240
243
  tempDir(subPath) {
241
- return new TempDir(path.resolve(this.path, subPath));
244
+ return new TempDir(path.resolve(this.path, this.notAbsolute(subPath)));
242
245
  }
243
- sanitize(name) {
244
- return sanitizeFilename(name.replace("https://", "").replace("www.", ""), { replacement: "_" }).slice(-200);
246
+ sanitize(filename) {
247
+ return sanitizeFilename(filename.replace("https://", "").replace("www.", ""), { replacement: "_" }).slice(-200);
245
248
  }
246
249
  /**
247
- * @param base - The file name with extension
250
+ * @param base - The file base (name and extension)
248
251
  * @example
249
252
  * const folder = new Dir('example');
250
253
  * const filepath = folder.resolve('file.json');
@@ -256,27 +259,38 @@ var Dir = class Dir {
256
259
  file(base) {
257
260
  return new File(this.filepath(base));
258
261
  }
262
+ get files() {
263
+ return fs.readdirSync(this.path).map((filename) => this.file(filename));
264
+ }
259
265
  };
260
266
  /**
261
- * Extends Dir class with method to `clear()` contents
267
+ * Extends Dir class with method to `clear()` contents.
268
+ * Default path: `./.${Date.now()}`
262
269
  */
263
270
  var TempDir = class extends Dir {
271
+ constructor(inputPath = `./.${Date.now()}`) {
272
+ super(inputPath);
273
+ }
274
+ /**
275
+ * > ⚠️ Warning! This deletes the directory, make sure it's not
276
+ */
264
277
  clear() {
265
278
  fs.rmSync(this.path, {
266
279
  recursive: true,
267
280
  force: true
268
281
  });
269
- this.create();
282
+ fs.mkdirSync(this.path, { recursive: true });
270
283
  }
271
284
  };
272
285
  /**
273
- * Common temp dir location
286
+ * Creates a '.temp' directory in current working directory
274
287
  */
275
- const temp = new TempDir(".temp");
288
+ function temp() {
289
+ return new TempDir(".temp");
290
+ }
276
291
 
277
292
  //#endregion
278
293
  //#region src/Cache.ts
279
- const cacheDir = temp.dir("cache");
280
294
  /**
281
295
  * Save data to a local file with an expiration.
282
296
  * Fresh/stale data is returned with a flag for if it's fresh or not,
@@ -286,7 +300,7 @@ var Cache = class {
286
300
  file;
287
301
  ttl;
288
302
  constructor(key, ttl, initialData) {
289
- this.file = cacheDir.file(key).json();
303
+ this.file = new TempDir(".cache").file(key).json();
290
304
  this.ttl = typeof ttl === "number" ? { minutes: ttl } : ttl;
291
305
  if (initialData) this.write(initialData);
292
306
  }
@@ -409,6 +423,58 @@ var Fetcher = class {
409
423
  }
410
424
  };
411
425
 
426
+ //#endregion
427
+ //#region src/Format.ts
428
+ /**
429
+ * Helpers for formatting dates, times, and numbers as strings
430
+ */
431
+ var Format = class {
432
+ /**
433
+ * date-fns format() with some shortcuts
434
+ * @param formatStr
435
+ * 'iso' to get ISO date, 'ymd' to format as 'yyyy-MM-dd', full options: https://date-fns.org/v4.1.0/docs/format
436
+ */
437
+ static date(formatStr = "iso", d = /* @__PURE__ */ new Date()) {
438
+ if (formatStr === "iso") return formatISO(d);
439
+ if (formatStr === "ymd") return format(d, "yyyy-MM-dd");
440
+ return format(d, formatStr);
441
+ }
442
+ /**
443
+ * Round a number to a specific set of places
444
+ */
445
+ static round(n, places = 0) {
446
+ return new Intl.NumberFormat("en-US", { maximumFractionDigits: places }).format(n);
447
+ }
448
+ /**
449
+ * Make millisecond durations actually readable (eg "123ms", "3.56s", "1m 34s", "3h 24m", "2d 4h")
450
+ */
451
+ static ms(ms) {
452
+ if (ms < 1e3) return `${this.round(ms)}ms`;
453
+ const s = ms / 1e3;
454
+ if (s < 60) return `${this.round(s, 2)}s`;
455
+ const m = Math.floor(s / 60);
456
+ if (m < 60) return `${m}m ${Math.floor(s) % 60}s`;
457
+ const h = Math.floor(m / 60);
458
+ if (h < 24) return `${h}h ${m % 60}m`;
459
+ return `${Math.floor(h / 24)}d ${h % 24}h`;
460
+ }
461
+ static bytes(b) {
462
+ const labels = [
463
+ "b",
464
+ "KB",
465
+ "MB",
466
+ "GB",
467
+ "TB"
468
+ ];
469
+ let factor = 0;
470
+ while (b >= 1024 && labels[factor + 1]) {
471
+ b = b / 1024;
472
+ factor++;
473
+ }
474
+ return `${this.round(b, 2)} ${labels[factor]}`;
475
+ }
476
+ };
477
+
412
478
  //#endregion
413
479
  //#region src/Log.ts
414
480
  var Log = class {
@@ -417,11 +483,11 @@ var Log = class {
417
483
  * Gcloud parses JSON in stdout
418
484
  */
419
485
  static #toGcloud(entry) {
420
- if (entry.details?.length === 1) console.log(JSON.stringify({
486
+ if (entry.details?.length === 1) console.log(JSON.stringify(snapshot({
421
487
  ...entry,
422
488
  details: entry.details[0]
423
- }));
424
- else console.log(JSON.stringify(entry));
489
+ })));
490
+ else console.log(JSON.stringify(snapshot(entry)));
425
491
  }
426
492
  /**
427
493
  * Includes colors and better inspection for logging during dev
@@ -464,10 +530,9 @@ var Log = class {
464
530
  }
465
531
  /**
466
532
  * Handle first argument being a string or an object with a 'message' prop
467
- * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
468
533
  */
469
534
  static prepare(...input) {
470
- let [first, ...rest] = input.map((i) => snapshot(i));
535
+ let [first, ...rest] = input;
471
536
  if (typeof first === "string") return {
472
537
  message: first,
473
538
  details: rest
@@ -569,5 +634,5 @@ var TypeWriter = class {
569
634
  };
570
635
 
571
636
  //#endregion
572
- export { Cache, Dir, Fetcher, File, FileType, FileTypeCsv, FileTypeJson, FileTypeNdjson, Log, TempDir, TypeWriter, snapshot, temp, timeout };
637
+ export { Cache, Dir, Fetcher, File, FileType, FileTypeCsv, FileTypeJson, FileTypeNdjson, Format, Log, TempDir, TypeWriter, snapshot, temp, timeout };
573
638
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["output: Record<string, any>","obj: Record<string, any>","parsed: Row[]","#parseVal","params: [string, string][]","timeout","#toGcloud","#toConsole","#log"],"sources":["../src/snapshot.ts","../src/File.ts","../src/Dir.ts","../src/Cache.ts","../src/Fetcher.ts","../src/Log.ts","../src/timeout.ts","../src/TypeWriter.ts"],"sourcesContent":["import { isObjectLike } from 'lodash-es';\n\n/**\n * Allows special objects (Error, Headers, Set) to be included in JSON.stringify output\n * functions are removed\n */\nexport function snapshot(i: unknown, max = 50, depth = 0): any {\n if (Array.isArray(i)) {\n if (depth === max) return [];\n return i.map((c) => snapshot(c, max, depth + 1));\n }\n if (typeof i === 'function') return undefined;\n if (!isObjectLike(i)) return i;\n\n if (depth === max) return {};\n let output: Record<string, any> = {};\n // @ts-ignore If it has an 'entries' function, use that for looping (eg. Set, Map, Headers)\n if (typeof i.entries === 'function') {\n // @ts-ignore\n for (let [k, v] of i.entries()) {\n output[k] = snapshot(v, max, depth + 1);\n }\n return output;\n }\n\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Enumerability_and_ownership_of_properties\n\n // Get Enumerable, inherited properties\n const obj: Record<string, any> = i!;\n for (let key in obj) {\n output[key] = snapshot(obj[key], max, depth + 1);\n }\n\n // Get Non-enumberable, own properties\n Object.getOwnPropertyNames(obj).forEach((key) => {\n output[key] = snapshot(obj[key], max, depth + 1);\n });\n\n return output;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { Readable } from 'node:stream';\nimport { finished } from 'node:stream/promises';\nimport { writeToStream, parseStream } from 'fast-csv';\nimport { snapshot } from './snapshot.ts';\n\n/**\n * WARNING: API will change!\n */\nexport class File {\n path;\n root;\n dir;\n base;\n ext;\n name;\n\n constructor(filepath: string) {\n this.path = filepath;\n const { root, dir, base, ext, name } = path.parse(filepath);\n this.root = root;\n this.dir = dir;\n this.base = base;\n this.ext = ext;\n this.name = name;\n }\n\n get exists() {\n return fs.existsSync(this.path);\n }\n\n delete() {\n fs.rmSync(this.path, { force: true });\n }\n\n read() {\n return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;\n }\n\n /**\n * @returns lines as strings, removes trailing '\\n'\n */\n lines() {\n const contents = (this.read() || '').split('\\n');\n return contents.slice(0, contents.length - 1);\n }\n\n get readStream() {\n return this.exists ? fs.createReadStream(this.path) : Readable.from([]);\n }\n\n get writeStream() {\n fs.mkdirSync(path.parse(this.path).dir, { recursive: true });\n return fs.createWriteStream(this.path);\n }\n\n write(contents: string | ReadableStream) {\n fs.mkdirSync(path.parse(this.path).dir, { recursive: true });\n if (typeof contents === 'string') return fs.writeFileSync(this.path, contents);\n if (contents instanceof ReadableStream) return finished(Readable.from(contents).pipe(this.writeStream));\n throw new Error(`Invalid content type: ${typeof contents}`);\n }\n\n /**\n * creates file if it doesn't exist, appends string or array of strings as new lines.\n * File always ends with '\\n', so contents don't need to be read before appending\n */\n append(lines: string | string[]) {\n if (!this.exists) this.write('');\n const contents = Array.isArray(lines) ? lines.join('\\n') : lines;\n fs.appendFileSync(this.path, contents + '\\n');\n }\n\n static get FileType() {\n return FileType;\n }\n\n json<T>(contents?: T) {\n return new FileTypeJson<T>(this.path, contents);\n }\n\n static get json() {\n return FileTypeJson;\n }\n\n ndjson<T extends object>(lines?: T | T[]) {\n return new FileTypeNdjson<T>(this.path, lines);\n }\n\n static get ndjson() {\n return FileTypeNdjson;\n }\n\n async csv<T extends object>(rows?: T[], keys?: (keyof T)[]) {\n const csvFile = new FileTypeCsv<T>(this.path);\n if (rows) await csvFile.write(rows, keys);\n return csvFile;\n }\n\n static get csv() {\n return FileTypeCsv;\n }\n}\n\n/**\n * A generic file adaptor, extended by specific file type implementations\n */\nexport class FileType {\n file;\n\n constructor(filepath: string, contents?: string) {\n this.file = new File(filepath);\n if (contents) this.file.write(contents);\n }\n\n get exists() {\n return this.file.exists;\n }\n\n get path() {\n return this.file.path;\n }\n\n delete() {\n this.file.delete();\n }\n}\n\n/**\n * A .json file that maintains data type when reading/writing.\n * This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.\n */\nexport class FileTypeJson<T> extends FileType {\n constructor(filepath: string, contents?: T) {\n super(filepath.endsWith('.json') ? filepath : filepath + '.json');\n if (contents) this.write(contents);\n }\n\n read() {\n const contents = this.file.read();\n return contents ? (JSON.parse(contents) as T) : undefined;\n }\n\n write(contents: T) {\n this.file.write(JSON.stringify(snapshot(contents), null, 2));\n }\n}\n\n/**\n * New-line delimited json file (.ndjson)\n * @see https://jsonltools.com/ndjson-format-specification\n */\nexport class FileTypeNdjson<T extends object> extends FileType {\n constructor(filepath: string, lines?: T | T[]) {\n super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');\n if (lines) this.append(lines);\n }\n\n append(lines: T | T[]) {\n this.file.append(\n Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines)),\n );\n }\n\n lines() {\n return this.file.lines().map((l) => JSON.parse(l) as T);\n }\n}\n\ntype Key<T extends object> = keyof T;\n\n/**\n * Comma separated values (.csv).\n * Input rows as objects, keys are used as column headers\n */\nexport class FileTypeCsv<Row extends object> extends FileType {\n constructor(filepath: string) {\n super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');\n }\n\n async write(rows: Row[], keys?: Key<Row>[]) {\n const headerSet = new Set<Key<Row>>();\n if (keys) {\n for (const key of keys) headerSet.add(key);\n } else {\n for (const row of rows) {\n for (const key in row) headerSet.add(key);\n }\n }\n const headers = Array.from(headerSet);\n const outRows = rows.map((row) => headers.map((key) => row[key]));\n return finished(writeToStream(this.file.writeStream, [headers, ...outRows]));\n }\n\n #parseVal(val: string) {\n if (val.toLowerCase() === 'false') return false;\n if (val.toLowerCase() === 'true') return true;\n if (val.length === 0) return null;\n if (/^[\\.0-9]+$/.test(val)) return Number(val);\n return val;\n }\n\n async read() {\n return new Promise<Row[]>((resolve, reject) => {\n const parsed: Row[] = [];\n parseStream(this.file.readStream, { headers: true })\n .on('error', (e) => reject(e))\n .on('data', (raw: Record<Key<Row>, string>) => {\n parsed.push(\n Object.entries(raw).reduce(\n (all, [key, val]) => ({\n ...all,\n [key]: this.#parseVal(val as string),\n }),\n {} as Row,\n ),\n );\n })\n .on('end', () => resolve(parsed));\n });\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport sanitizeFilename from 'sanitize-filename';\nimport { File } from './File.ts';\n\n/**\n * Reference to a specific directory with helpful methods for resolving filepaths,\n * sanitizing filenames, and saving files.\n */\nexport class Dir {\n path;\n\n /**\n * @param path can be relative to workspace or absolute\n */\n constructor(_path = './') {\n this.path = path.resolve(_path);\n }\n\n create() {\n fs.mkdirSync(this.path, { recursive: true });\n }\n\n /**\n * Create a new Dir inside the current Dir\n * @param subPath to create in current Dir\n * @example\n * const folder = new Dir('example');\n * // folder.path = './example'\n * const child = folder.dir('path/to/dir');\n * // child.path = './example/path/to/dir'\n */\n dir(subPath: string) {\n return new Dir(path.resolve(this.path, subPath));\n }\n\n tempDir(subPath: string) {\n return new TempDir(path.resolve(this.path, subPath));\n }\n\n sanitize(name: string) {\n return sanitizeFilename(name.replace('https://', '').replace('www.', ''), { replacement: '_' }).slice(-200);\n }\n\n /**\n * @param base - The file name with extension\n * @example\n * const folder = new Dir('example');\n * const filepath = folder.resolve('file.json');\n * // 'example/file.json'\n */\n filepath(base: string) {\n return path.resolve(this.path, this.sanitize(base));\n }\n\n file(base: string) {\n return new File(this.filepath(base));\n }\n}\n\n/**\n * Extends Dir class with method to `clear()` contents\n */\nexport class TempDir extends Dir {\n clear() {\n fs.rmSync(this.path, { recursive: true, force: true });\n this.create();\n }\n}\n\n/**\n * Common temp dir location\n */\nexport const temp = new TempDir('.temp');\n","import { type Duration, isAfter, add } from 'date-fns';\nimport { temp } from './Dir.ts';\n\nconst cacheDir = temp.dir('cache');\n\n/**\n * Save data to a local file with an expiration.\n * Fresh/stale data is returned with a flag for if it's fresh or not,\n * so stale data can still be used if needed.\n */\nexport class Cache<T> {\n file;\n ttl;\n\n constructor(key: string, ttl: number | Duration, initialData?: T) {\n this.file = cacheDir.file(key).json<{ savedAt: string; data: T }>();\n this.ttl = typeof ttl === 'number' ? { minutes: ttl } : ttl;\n if (initialData) this.write(initialData);\n }\n\n write(data: T) {\n this.file.write({ savedAt: new Date().toUTCString(), data });\n }\n\n read(): [T | undefined, boolean] {\n const { savedAt, data } = this.file.read() || {};\n const isFresh = Boolean(savedAt && isAfter(add(savedAt, this.ttl), new Date()));\n return [data, isFresh];\n }\n}\n","import { merge } from 'lodash-es';\nimport extractDomain from 'extract-domain';\n\nexport type Route = string | URL;\n\ntype QueryVal = string | number | boolean | null | undefined;\nexport type Query = Record<string, QueryVal | QueryVal[]>;\n\nexport type FetchOptions = RequestInit & {\n base?: string;\n query?: Query;\n headers?: Record<string, string>;\n data?: any;\n timeout?: number;\n retries?: number;\n retryDelay?: number;\n};\n\n/**\n * Fetcher provides a quick way to set up a basic API connection\n * with options applied to every request.\n * Includes basic methods for requesting and parsing responses\n */\nexport class Fetcher {\n defaultOptions;\n\n constructor(opts: FetchOptions = {}) {\n this.defaultOptions = {\n timeout: 60000,\n retries: 0,\n retryDelay: 3000,\n ...opts,\n };\n }\n\n /**\n * Build URL with URLSearchParams if query is provided.\n * Also returns domain, to help with cookies\n */\n buildUrl(route: Route, opts: FetchOptions = {}): [URL, string] {\n const mergedOptions = merge({}, this.defaultOptions, opts);\n const params: [string, string][] = [];\n Object.entries(mergedOptions.query || {}).forEach(([key, val]) => {\n if (val === undefined) return;\n if (Array.isArray(val)) {\n val.forEach((v) => {\n params.push([key, `${v}`]);\n });\n } else {\n params.push([key, `${val}`]);\n }\n });\n const search = params.length > 0 ? '?' + new URLSearchParams(params).toString() : '';\n const url = new URL(route + search, this.defaultOptions.base);\n const domain = extractDomain(url.href) as string;\n return [url, domain];\n }\n\n /**\n * Merges options to get headers. Useful when extending the Fetcher class to add custom auth.\n */\n buildHeaders(route: Route, opts: FetchOptions = {}) {\n const { headers } = merge({}, this.defaultOptions, opts);\n return headers || {};\n }\n\n /**\n * Builds request, merging defaultOptions and provided options.\n * Includes Abort signal for timeout\n */\n buildRequest(route: Route, opts: FetchOptions = {}): [Request, FetchOptions, string] {\n const mergedOptions = merge({}, this.defaultOptions, opts);\n const { query, data, timeout, retries, ...init } = mergedOptions;\n init.headers = this.buildHeaders(route, mergedOptions);\n if (data) {\n init.headers['content-type'] = init.headers['content-type'] || 'application/json';\n init.method = init.method || 'POST';\n init.body = JSON.stringify(data);\n }\n if (timeout) {\n init.signal = AbortSignal.timeout(timeout);\n }\n const [url, domain] = this.buildUrl(route, mergedOptions);\n const req = new Request(url, init);\n return [req, mergedOptions, domain];\n }\n\n /**\n * Builds and performs the request, merging provided options with defaultOptions.\n * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body.\n * Retries on local or network error, with increasing backoff.\n */\n async fetch(route: Route, opts: FetchOptions = {}): Promise<[Response, Request]> {\n const [_req, options] = this.buildRequest(route, opts);\n const maxAttempts = (options.retries || 0) + 1;\n let attempt = 0;\n while (attempt < maxAttempts) {\n attempt++;\n const [req] = this.buildRequest(route, opts);\n const res = await fetch(req)\n .then((r) => {\n if (!r.ok) throw new Error(r.statusText);\n return r;\n })\n .catch(async (error) => {\n if (attempt < maxAttempts) {\n const wait = attempt * 3000;\n console.warn(`${req.method} ${req.url} (attempt ${attempt} of ${maxAttempts})`, error);\n await new Promise((resolve) => setTimeout(resolve, wait));\n } else {\n throw new Error(error);\n }\n });\n if (res) return [res, req];\n }\n throw new Error(`Failed to fetch ${_req.url}`);\n }\n\n async fetchText(route: Route, opts: FetchOptions = {}): Promise<[string, Response, Request]> {\n return this.fetch(route, opts).then(async ([res, req]) => {\n const text = await res.text();\n return [text, res, req];\n });\n }\n\n async fetchJson<T>(route: Route, opts: FetchOptions = {}): Promise<[T, Response, Request]> {\n return this.fetchText(route, opts).then(([txt, res, req]) => [JSON.parse(txt) as T, res, req]);\n }\n}\n","import { inspect } from 'node:util';\nimport { isObjectLike } from 'lodash-es';\nimport chalk, { type ChalkInstance } from 'chalk';\nimport { snapshot } from './snapshot.ts';\n\ntype Severity = 'DEFAULT' | 'DEBUG' | 'INFO' | 'NOTICE' | 'WARNING' | 'ERROR' | 'CRITICAL' | 'ALERT' | 'EMERGENCY';\n\ntype Options = {\n severity: Severity;\n color: ChalkInstance;\n};\n\ntype Entry = {\n message?: string;\n severity: Severity;\n details?: unknown[];\n};\n\nexport class Log {\n // Only silence logs when THIS package is running its own tests\n static isTest = process.env.npm_package_name === '@brianbuie/node-kit' && process.env.npm_lifecycle_event === 'test';\n\n /**\n * Gcloud parses JSON in stdout\n */\n static #toGcloud(entry: Entry) {\n if (entry.details?.length === 1) {\n console.log(JSON.stringify({ ...entry, details: entry.details[0] }));\n } else {\n console.log(JSON.stringify(entry));\n }\n }\n\n /**\n * Includes colors and better inspection for logging during dev\n */\n static #toConsole(entry: Entry, color: ChalkInstance) {\n if (entry.message) console.log(color(`[${entry.severity}] ${entry.message}`));\n entry.details?.forEach((detail) => {\n console.log(inspect(detail, { depth: 10, breakLength: 100, compact: true, colors: true }));\n });\n }\n\n static #log(options: Options, ...input: unknown[]) {\n const { message, details } = this.prepare(...input);\n // https://cloud.google.com/run/docs/container-contract#env-vars\n const isGcloud = process.env.K_SERVICE !== undefined || process.env.CLOUD_RUN_JOB !== undefined;\n if (isGcloud) {\n this.#toGcloud({ message, severity: options.severity, details });\n return { message, details, options };\n }\n // Hide output while testing this package\n if (!this.isTest) {\n this.#toConsole({ message, severity: options.severity, details }, options.color);\n }\n return { message, details, options };\n }\n\n /**\n * Handle first argument being a string or an object with a 'message' prop\n * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output\n */\n static prepare(...input: unknown[]): { message?: string; details: unknown[] } {\n let [first, ...rest] = input.map((i) => snapshot(i));\n if (typeof first === 'string') return { message: first, details: rest };\n // @ts-ignore\n if (isObjectLike(first) && typeof first['message'] === 'string') {\n const { message, ...firstDetails } = first as { message: string };\n return { message, details: [firstDetails, ...rest] };\n }\n return { details: input };\n }\n\n /**\n * Logs error details before throwing\n */\n static error(...input: unknown[]) {\n const { message } = this.#log({ severity: 'ERROR', color: chalk.red }, ...input);\n throw new Error(message);\n }\n\n static warn(...input: unknown[]) {\n return this.#log({ severity: 'WARNING', color: chalk.yellow }, ...input);\n }\n\n static notice(...input: unknown[]) {\n return this.#log({ severity: 'NOTICE', color: chalk.cyan }, ...input);\n }\n\n static info(...input: unknown[]) {\n return this.#log({ severity: 'INFO', color: chalk.white }, ...input);\n }\n\n static debug(...input: unknown[]) {\n const debugging = process.argv.some((arg) => arg.includes('--debug')) || process.env.DEBUG !== undefined;\n if (debugging || process.env.NODE_ENV !== 'production') {\n return this.#log({ severity: 'DEBUG', color: chalk.gray }, ...input);\n }\n }\n}\n","export async function timeout(ms: number) {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","import * as fs from 'node:fs';\nimport { merge } from 'lodash-es';\nimport * as qt from 'quicktype-core';\n\nexport class TypeWriter {\n moduleName;\n input = qt.jsonInputForTargetLanguage('typescript');\n outDir;\n qtSettings;\n\n constructor(moduleName: string, settings: { outDir?: string } & Partial<qt.Options> = {}) {\n this.moduleName = moduleName;\n const { outDir, ...qtSettings } = settings;\n this.outDir = outDir || './types';\n const defaultSettings = {\n lang: 'typescript',\n rendererOptions: {\n 'just-types': true,\n 'prefer-types': true,\n },\n inferEnums: false,\n inferDateTimes: false,\n };\n this.qtSettings = merge(defaultSettings, qtSettings);\n }\n\n async addMember(name: string, _samples: any[]) {\n const samples = _samples.map((s) => (typeof s === 'string' ? s : JSON.stringify(s)));\n await this.input.addSource({ name, samples });\n }\n\n async toString() {\n const inputData = new qt.InputData();\n inputData.addInput(this.input);\n const result = await qt.quicktype({\n inputData,\n ...this.qtSettings,\n });\n return result.lines.join('\\n');\n }\n\n async toFile() {\n const result = await this.toString();\n fs.mkdirSync(this.outDir, { recursive: true });\n fs.writeFileSync(`${this.outDir}/${this.moduleName}.d.ts`, result);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["output: Record<string, any>","obj: Record<string, any>","parsed: Row[]","#parseVal","params: [string, string][]","timeout","#toGcloud","#toConsole","#log"],"sources":["../src/snapshot.ts","../src/File.ts","../src/Dir.ts","../src/Cache.ts","../src/Fetcher.ts","../src/Format.ts","../src/Log.ts","../src/timeout.ts","../src/TypeWriter.ts"],"sourcesContent":["import { isObjectLike } from 'lodash-es';\n\n/**\n * Allows special objects (Error, Headers, Set) to be included in JSON.stringify output\n * functions are removed\n */\nexport function snapshot(i: unknown, max = 50, depth = 0): any {\n if (Array.isArray(i)) {\n if (depth === max) return [];\n return i.map((c) => snapshot(c, max, depth + 1));\n }\n if (typeof i === 'function') return undefined;\n if (!isObjectLike(i)) return i;\n\n if (depth === max) return {};\n let output: Record<string, any> = {};\n // @ts-ignore If it has an 'entries' function, use that for looping (eg. Set, Map, Headers)\n if (typeof i.entries === 'function') {\n // @ts-ignore\n for (let [k, v] of i.entries()) {\n output[k] = snapshot(v, max, depth + 1);\n }\n return output;\n }\n\n // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Enumerability_and_ownership_of_properties\n\n // Get Enumerable, inherited properties\n const obj: Record<string, any> = i!;\n for (let key in obj) {\n output[key] = snapshot(obj[key], max, depth + 1);\n }\n\n // Get Non-enumberable, own properties\n Object.getOwnPropertyNames(obj).forEach((key) => {\n output[key] = snapshot(obj[key], max, depth + 1);\n });\n\n return output;\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { Readable } from 'node:stream';\nimport { finished } from 'node:stream/promises';\nimport { writeToStream, parseStream } from 'fast-csv';\nimport { snapshot } from './snapshot.ts';\n\n/**\n * > ⚠️ WARNING: API will change!\n */\nexport class File {\n path;\n root;\n dir;\n base;\n ext;\n name;\n\n constructor(filepath: string) {\n this.path = filepath;\n const { root, dir, base, ext, name } = path.parse(filepath);\n this.root = root;\n this.dir = dir;\n this.base = base;\n this.ext = ext;\n this.name = name;\n }\n\n get exists() {\n return fs.existsSync(this.path);\n }\n\n delete() {\n fs.rmSync(this.path, { force: true });\n }\n\n read() {\n return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;\n }\n\n /**\n * @returns lines as strings, removes trailing '\\n'\n */\n lines() {\n const contents = (this.read() || '').split('\\n');\n return contents.slice(0, contents.length - 1);\n }\n\n get readStream() {\n return this.exists ? fs.createReadStream(this.path) : Readable.from([]);\n }\n\n get writeStream() {\n fs.mkdirSync(path.parse(this.path).dir, { recursive: true });\n return fs.createWriteStream(this.path);\n }\n\n write(contents: string | ReadableStream) {\n fs.mkdirSync(path.parse(this.path).dir, { recursive: true });\n if (typeof contents === 'string') return fs.writeFileSync(this.path, contents);\n if (contents instanceof ReadableStream) return finished(Readable.from(contents).pipe(this.writeStream));\n throw new Error(`Invalid content type: ${typeof contents}`);\n }\n\n /**\n * creates file if it doesn't exist, appends string or array of strings as new lines.\n * File always ends with '\\n', so contents don't need to be read before appending\n */\n append(lines: string | string[]) {\n if (!this.exists) this.write('');\n const contents = Array.isArray(lines) ? lines.join('\\n') : lines;\n fs.appendFileSync(this.path, contents + '\\n');\n }\n\n static get FileType() {\n return FileType;\n }\n\n json<T>(contents?: T) {\n return new FileTypeJson<T>(this.path, contents);\n }\n\n static get json() {\n return FileTypeJson;\n }\n\n ndjson<T extends object>(lines?: T | T[]) {\n return new FileTypeNdjson<T>(this.path, lines);\n }\n\n static get ndjson() {\n return FileTypeNdjson;\n }\n\n async csv<T extends object>(rows?: T[], keys?: (keyof T)[]) {\n const csvFile = new FileTypeCsv<T>(this.path);\n if (rows) await csvFile.write(rows, keys);\n return csvFile;\n }\n\n static get csv() {\n return FileTypeCsv;\n }\n}\n\n/**\n * A generic file adaptor, extended by specific file type implementations\n */\nexport class FileType {\n file;\n\n constructor(filepath: string, contents?: string) {\n this.file = new File(filepath);\n if (contents) this.file.write(contents);\n }\n\n get exists() {\n return this.file.exists;\n }\n\n get path() {\n return this.file.path;\n }\n\n delete() {\n this.file.delete();\n }\n}\n\n/**\n * A .json file that maintains data type when reading/writing.\n * This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.\n */\nexport class FileTypeJson<T> extends FileType {\n constructor(filepath: string, contents?: T) {\n super(filepath.endsWith('.json') ? filepath : filepath + '.json');\n if (contents) this.write(contents);\n }\n\n read() {\n const contents = this.file.read();\n return contents ? (JSON.parse(contents) as T) : undefined;\n }\n\n write(contents: T) {\n this.file.write(JSON.stringify(snapshot(contents), null, 2));\n }\n}\n\n/**\n * New-line delimited json file (.ndjson)\n * @see https://jsonltools.com/ndjson-format-specification\n */\nexport class FileTypeNdjson<T extends object> extends FileType {\n constructor(filepath: string, lines?: T | T[]) {\n super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');\n if (lines) this.append(lines);\n }\n\n append(lines: T | T[]) {\n this.file.append(\n Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines)),\n );\n }\n\n lines() {\n return this.file.lines().map((l) => JSON.parse(l) as T);\n }\n}\n\ntype Key<T extends object> = keyof T;\n\n/**\n * Comma separated values (.csv).\n * Input rows as objects, keys are used as column headers\n */\nexport class FileTypeCsv<Row extends object> extends FileType {\n constructor(filepath: string) {\n super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');\n }\n\n async write(rows: Row[], keys?: Key<Row>[]) {\n const headerSet = new Set<Key<Row>>();\n if (keys) {\n for (const key of keys) headerSet.add(key);\n } else {\n for (const row of rows) {\n for (const key in row) headerSet.add(key);\n }\n }\n const headers = Array.from(headerSet);\n const outRows = rows.map((row) => headers.map((key) => row[key]));\n return finished(writeToStream(this.file.writeStream, [headers, ...outRows]));\n }\n\n #parseVal(val: string) {\n if (val.toLowerCase() === 'false') return false;\n if (val.toLowerCase() === 'true') return true;\n if (val.length === 0) return null;\n if (/^[\\.0-9]+$/.test(val)) return Number(val);\n return val;\n }\n\n async read() {\n return new Promise<Row[]>((resolve, reject) => {\n const parsed: Row[] = [];\n parseStream(this.file.readStream, { headers: true })\n .on('error', (e) => reject(e))\n .on('data', (raw: Record<Key<Row>, string>) => {\n parsed.push(\n Object.entries(raw).reduce(\n (all, [key, val]) => ({\n ...all,\n [key]: this.#parseVal(val as string),\n }),\n {} as Row,\n ),\n );\n })\n .on('end', () => resolve(parsed));\n });\n }\n}\n","import * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport sanitizeFilename from 'sanitize-filename';\nimport { File } from './File.ts';\n\n/**\n * Reference to a specific directory with methods to create and list files.\n * Created immediately if it doesn't exist.\n * Default path: './'\n */\nexport class Dir {\n path;\n\n /**\n * @param path can be relative to workspace or absolute\n */\n constructor(inputPath = './') {\n this.path = path.resolve(inputPath);\n fs.mkdirSync(this.path, { recursive: true });\n }\n\n notAbsolute(subPath: string) {\n if (path.isAbsolute(subPath)) throw new Error(`Absolute path provided: \"${subPath}\"`);\n return subPath;\n }\n\n /**\n * Create a new Dir inside the current Dir\n * @param subPath relative path to create\n * @example\n * const folder = new Dir('example');\n * // folder.path = '/absolute/path/to/example'\n * const child = folder.dir('path/to/dir');\n * // child.path = '/absolute/path/to/example/path/to/dir'\n */\n dir(subPath: string) {\n return new Dir(path.resolve(this.path, this.notAbsolute(subPath)));\n }\n\n tempDir(subPath: string) {\n return new TempDir(path.resolve(this.path, this.notAbsolute(subPath)));\n }\n\n sanitize(filename: string) {\n const notUrl = filename.replace('https://', '').replace('www.', '');\n return sanitizeFilename(notUrl, { replacement: '_' }).slice(-200);\n }\n\n /**\n * @param base - The file base (name and extension)\n * @example\n * const folder = new Dir('example');\n * const filepath = folder.resolve('file.json');\n * // 'example/file.json'\n */\n filepath(base: string) {\n return path.resolve(this.path, this.sanitize(base));\n }\n\n file(base: string) {\n return new File(this.filepath(base));\n }\n\n get files() {\n return fs.readdirSync(this.path).map((filename) => this.file(filename));\n }\n}\n\n/**\n * Extends Dir class with method to `clear()` contents.\n * Default path: `./.${Date.now()}`\n */\nexport class TempDir extends Dir {\n constructor(inputPath = `./.${Date.now()}`) {\n super(inputPath);\n }\n\n /**\n * > ⚠️ Warning! This deletes the directory, make sure it's not\n */\n clear() {\n fs.rmSync(this.path, { recursive: true, force: true });\n fs.mkdirSync(this.path, { recursive: true });\n }\n}\n\n/**\n * Creates a '.temp' directory in current working directory\n */\nexport function temp() {\n return new TempDir('.temp');\n}\n","import { type Duration, isAfter, add } from 'date-fns';\nimport { TempDir } from './Dir.ts';\n\n/**\n * Save data to a local file with an expiration.\n * Fresh/stale data is returned with a flag for if it's fresh or not,\n * so stale data can still be used if needed.\n */\nexport class Cache<T> {\n file;\n ttl;\n\n constructor(key: string, ttl: number | Duration, initialData?: T) {\n const dir = new TempDir('.cache');\n this.file = dir.file(key).json<{ savedAt: string; data: T }>();\n this.ttl = typeof ttl === 'number' ? { minutes: ttl } : ttl;\n if (initialData) this.write(initialData);\n }\n\n write(data: T) {\n this.file.write({ savedAt: new Date().toUTCString(), data });\n }\n\n read(): [T | undefined, boolean] {\n const { savedAt, data } = this.file.read() || {};\n const isFresh = Boolean(savedAt && isAfter(add(savedAt, this.ttl), new Date()));\n return [data, isFresh];\n }\n}\n","import { merge } from 'lodash-es';\nimport extractDomain from 'extract-domain';\n\nexport type Route = string | URL;\n\ntype QueryVal = string | number | boolean | null | undefined;\nexport type Query = Record<string, QueryVal | QueryVal[]>;\n\nexport type FetchOptions = RequestInit & {\n base?: string;\n query?: Query;\n headers?: Record<string, string>;\n data?: any;\n timeout?: number;\n retries?: number;\n retryDelay?: number;\n};\n\n/**\n * Fetcher provides a quick way to set up a basic API connection\n * with options applied to every request.\n * Includes basic methods for requesting and parsing responses\n */\nexport class Fetcher {\n defaultOptions;\n\n constructor(opts: FetchOptions = {}) {\n this.defaultOptions = {\n timeout: 60000,\n retries: 0,\n retryDelay: 3000,\n ...opts,\n };\n }\n\n /**\n * Build URL with URLSearchParams if query is provided.\n * Also returns domain, to help with cookies\n */\n buildUrl(route: Route, opts: FetchOptions = {}): [URL, string] {\n const mergedOptions = merge({}, this.defaultOptions, opts);\n const params: [string, string][] = [];\n Object.entries(mergedOptions.query || {}).forEach(([key, val]) => {\n if (val === undefined) return;\n if (Array.isArray(val)) {\n val.forEach((v) => {\n params.push([key, `${v}`]);\n });\n } else {\n params.push([key, `${val}`]);\n }\n });\n const search = params.length > 0 ? '?' + new URLSearchParams(params).toString() : '';\n const url = new URL(route + search, this.defaultOptions.base);\n const domain = extractDomain(url.href) as string;\n return [url, domain];\n }\n\n /**\n * Merges options to get headers. Useful when extending the Fetcher class to add custom auth.\n */\n buildHeaders(route: Route, opts: FetchOptions = {}) {\n const { headers } = merge({}, this.defaultOptions, opts);\n return headers || {};\n }\n\n /**\n * Builds request, merging defaultOptions and provided options.\n * Includes Abort signal for timeout\n */\n buildRequest(route: Route, opts: FetchOptions = {}): [Request, FetchOptions, string] {\n const mergedOptions = merge({}, this.defaultOptions, opts);\n const { query, data, timeout, retries, ...init } = mergedOptions;\n init.headers = this.buildHeaders(route, mergedOptions);\n if (data) {\n init.headers['content-type'] = init.headers['content-type'] || 'application/json';\n init.method = init.method || 'POST';\n init.body = JSON.stringify(data);\n }\n if (timeout) {\n init.signal = AbortSignal.timeout(timeout);\n }\n const [url, domain] = this.buildUrl(route, mergedOptions);\n const req = new Request(url, init);\n return [req, mergedOptions, domain];\n }\n\n /**\n * Builds and performs the request, merging provided options with defaultOptions.\n * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body.\n * Retries on local or network error, with increasing backoff.\n */\n async fetch(route: Route, opts: FetchOptions = {}): Promise<[Response, Request]> {\n const [_req, options] = this.buildRequest(route, opts);\n const maxAttempts = (options.retries || 0) + 1;\n let attempt = 0;\n while (attempt < maxAttempts) {\n attempt++;\n const [req] = this.buildRequest(route, opts);\n const res = await fetch(req)\n .then((r) => {\n if (!r.ok) throw new Error(r.statusText);\n return r;\n })\n .catch(async (error) => {\n if (attempt < maxAttempts) {\n const wait = attempt * 3000;\n console.warn(`${req.method} ${req.url} (attempt ${attempt} of ${maxAttempts})`, error);\n await new Promise((resolve) => setTimeout(resolve, wait));\n } else {\n throw new Error(error);\n }\n });\n if (res) return [res, req];\n }\n throw new Error(`Failed to fetch ${_req.url}`);\n }\n\n async fetchText(route: Route, opts: FetchOptions = {}): Promise<[string, Response, Request]> {\n return this.fetch(route, opts).then(async ([res, req]) => {\n const text = await res.text();\n return [text, res, req];\n });\n }\n\n async fetchJson<T>(route: Route, opts: FetchOptions = {}): Promise<[T, Response, Request]> {\n return this.fetchText(route, opts).then(([txt, res, req]) => [JSON.parse(txt) as T, res, req]);\n }\n}\n","import { format, formatISO, type DateArg } from 'date-fns';\n\n/**\n * Helpers for formatting dates, times, and numbers as strings\n */\nexport class Format {\n /**\n * date-fns format() with some shortcuts\n * @param formatStr\n * 'iso' to get ISO date, 'ymd' to format as 'yyyy-MM-dd', full options: https://date-fns.org/v4.1.0/docs/format\n */\n static date(formatStr: 'iso' | 'ymd' | string = 'iso', d: DateArg<Date> = new Date()) {\n if (formatStr === 'iso') return formatISO(d);\n if (formatStr === 'ymd') return format(d, 'yyyy-MM-dd');\n return format(d, formatStr);\n }\n\n /**\n * Round a number to a specific set of places\n */\n static round(n: number, places = 0) {\n return new Intl.NumberFormat('en-US', { maximumFractionDigits: places }).format(n);\n }\n\n /**\n * Make millisecond durations actually readable (eg \"123ms\", \"3.56s\", \"1m 34s\", \"3h 24m\", \"2d 4h\")\n */\n static ms(ms: number) {\n if (ms < 1000) return `${this.round(ms)}ms`;\n const s = ms / 1000;\n if (s < 60) return `${this.round(s, 2)}s`;\n const m = Math.floor(s / 60);\n if (m < 60) return `${m}m ${Math.floor(s) % 60}s`;\n const h = Math.floor(m / 60);\n if (h < 24) return `${h}h ${m % 60}m`;\n const d = Math.floor(h / 24);\n return `${d}d ${h % 24}h`;\n }\n\n static bytes(b: number) {\n const labels = ['b', 'KB', 'MB', 'GB', 'TB'];\n let factor = 0;\n while (b >= 1024 && labels[factor + 1]) {\n b = b / 1024;\n factor++;\n }\n return `${this.round(b, 2)} ${labels[factor]}`;\n }\n}\n","import { inspect } from 'node:util';\nimport { isObjectLike } from 'lodash-es';\nimport chalk, { type ChalkInstance } from 'chalk';\nimport { snapshot } from './snapshot.ts';\n\ntype Severity = 'DEFAULT' | 'DEBUG' | 'INFO' | 'NOTICE' | 'WARNING' | 'ERROR' | 'CRITICAL' | 'ALERT' | 'EMERGENCY';\n\ntype Options = {\n severity: Severity;\n color: ChalkInstance;\n};\n\ntype Entry = {\n message?: string;\n severity: Severity;\n details?: unknown[];\n};\n\nexport class Log {\n // Only silence logs when THIS package is running its own tests\n static isTest = process.env.npm_package_name === '@brianbuie/node-kit' && process.env.npm_lifecycle_event === 'test';\n\n /**\n * Gcloud parses JSON in stdout\n */\n static #toGcloud(entry: Entry) {\n if (entry.details?.length === 1) {\n console.log(JSON.stringify(snapshot({ ...entry, details: entry.details[0] })));\n } else {\n console.log(JSON.stringify(snapshot(entry)));\n }\n }\n\n /**\n * Includes colors and better inspection for logging during dev\n */\n static #toConsole(entry: Entry, color: ChalkInstance) {\n if (entry.message) console.log(color(`[${entry.severity}] ${entry.message}`));\n entry.details?.forEach((detail) => {\n console.log(inspect(detail, { depth: 10, breakLength: 100, compact: true, colors: true }));\n });\n }\n\n static #log(options: Options, ...input: unknown[]) {\n const { message, details } = this.prepare(...input);\n // https://cloud.google.com/run/docs/container-contract#env-vars\n const isGcloud = process.env.K_SERVICE !== undefined || process.env.CLOUD_RUN_JOB !== undefined;\n if (isGcloud) {\n this.#toGcloud({ message, severity: options.severity, details });\n return { message, details, options };\n }\n // Hide output while testing this package\n if (!this.isTest) {\n this.#toConsole({ message, severity: options.severity, details }, options.color);\n }\n return { message, details, options };\n }\n\n /**\n * Handle first argument being a string or an object with a 'message' prop\n */\n static prepare(...input: unknown[]): { message?: string; details: unknown[] } {\n let [first, ...rest] = input;\n if (typeof first === 'string') {\n return { message: first, details: rest };\n }\n // @ts-ignore\n if (isObjectLike(first) && typeof first['message'] === 'string') {\n const { message, ...firstDetails } = first as { message: string };\n return { message, details: [firstDetails, ...rest] };\n }\n return { details: input };\n }\n\n /**\n * Logs error details before throwing\n */\n static error(...input: unknown[]) {\n const { message } = this.#log({ severity: 'ERROR', color: chalk.red }, ...input);\n throw new Error(message);\n }\n\n static warn(...input: unknown[]) {\n return this.#log({ severity: 'WARNING', color: chalk.yellow }, ...input);\n }\n\n static notice(...input: unknown[]) {\n return this.#log({ severity: 'NOTICE', color: chalk.cyan }, ...input);\n }\n\n static info(...input: unknown[]) {\n return this.#log({ severity: 'INFO', color: chalk.white }, ...input);\n }\n\n static debug(...input: unknown[]) {\n const debugging = process.argv.some((arg) => arg.includes('--debug')) || process.env.DEBUG !== undefined;\n if (debugging || process.env.NODE_ENV !== 'production') {\n return this.#log({ severity: 'DEBUG', color: chalk.gray }, ...input);\n }\n }\n}\n","export async function timeout(ms: number) {\n return new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n}\n","import * as fs from 'node:fs';\nimport { merge } from 'lodash-es';\nimport * as qt from 'quicktype-core';\n\nexport class TypeWriter {\n moduleName;\n input = qt.jsonInputForTargetLanguage('typescript');\n outDir;\n qtSettings;\n\n constructor(moduleName: string, settings: { outDir?: string } & Partial<qt.Options> = {}) {\n this.moduleName = moduleName;\n const { outDir, ...qtSettings } = settings;\n this.outDir = outDir || './types';\n const defaultSettings = {\n lang: 'typescript',\n rendererOptions: {\n 'just-types': true,\n 'prefer-types': true,\n },\n inferEnums: false,\n inferDateTimes: false,\n };\n this.qtSettings = merge(defaultSettings, qtSettings);\n }\n\n async addMember(name: string, _samples: any[]) {\n const samples = _samples.map((s) => (typeof s === 'string' ? s : JSON.stringify(s)));\n await this.input.addSource({ name, samples });\n }\n\n async toString() {\n const inputData = new qt.InputData();\n inputData.addInput(this.input);\n const result = await qt.quicktype({\n inputData,\n ...this.qtSettings,\n });\n return result.lines.join('\\n');\n }\n\n async toFile() {\n const result = await this.toString();\n fs.mkdirSync(this.outDir, { recursive: true });\n fs.writeFileSync(`${this.outDir}/${this.moduleName}.d.ts`, result);\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,9 @@
1
1
  {
2
2
  "name": "@brianbuie/node-kit",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "license": "ISC",
5
+ "description": "Basic tools for Node.js projects",
6
+ "author": "Brian Buie <brian@buie.dev>",
5
7
  "repository": {
6
8
  "type": "git",
7
9
  "url": "git+https://github.com/brianbuie/node-kit.git"
@@ -22,7 +24,7 @@
22
24
  "files": [
23
25
  "src",
24
26
  "dist",
25
- "README.md"
27
+ "tsconfig.json"
26
28
  ],
27
29
  "engines": {
28
30
  "node": ">=24"
package/src/Cache.ts CHANGED
@@ -1,7 +1,5 @@
1
1
  import { type Duration, isAfter, add } from 'date-fns';
2
- import { temp } from './Dir.ts';
3
-
4
- const cacheDir = temp.dir('cache');
2
+ import { TempDir } from './Dir.ts';
5
3
 
6
4
  /**
7
5
  * Save data to a local file with an expiration.
@@ -13,7 +11,8 @@ export class Cache<T> {
13
11
  ttl;
14
12
 
15
13
  constructor(key: string, ttl: number | Duration, initialData?: T) {
16
- this.file = cacheDir.file(key).json<{ savedAt: string; data: T }>();
14
+ const dir = new TempDir('.cache');
15
+ this.file = dir.file(key).json<{ savedAt: string; data: T }>();
17
16
  this.ttl = typeof ttl === 'number' ? { minutes: ttl } : ttl;
18
17
  if (initialData) this.write(initialData);
19
18
  }
package/src/Dir.test.ts CHANGED
@@ -3,7 +3,7 @@ import assert from 'node:assert';
3
3
  import { Dir, TempDir, temp } from './Dir.ts';
4
4
 
5
5
  describe('Dir', () => {
6
- const testDir = temp.dir('dir-test');
6
+ const testDir = temp().dir('dir-test');
7
7
 
8
8
  it('Sanitizes filenames', () => {
9
9
  const name = testDir.sanitize(':/something/else.json');
@@ -18,14 +18,24 @@ describe('Dir', () => {
18
18
  assert(sub.path.includes(subPath));
19
19
  });
20
20
 
21
- it('TempDir.tempDir returns instance of TempDir', () => {
22
- const sub = temp.tempDir('example');
21
+ it('.tempDir returns instance of TempDir', () => {
22
+ const sub = temp().tempDir('example');
23
23
  assert(sub instanceof TempDir);
24
24
  });
25
25
 
26
+ it('.dir() and .tempDir() throw on absolute path input', () => {
27
+ const t = temp();
28
+ assert.throws(() => {
29
+ t.dir(t.path);
30
+ });
31
+ assert.throws(() => {
32
+ t.tempDir(t.path);
33
+ });
34
+ });
35
+
26
36
  it('Resolves filenames in folder', () => {
27
- const txt = temp.filepath('test.txt');
28
- assert(txt.includes(temp.path));
37
+ const txt = temp().filepath('test.txt');
38
+ assert(txt.includes(temp().path));
29
39
  assert(txt.includes('test.txt'));
30
40
  });
31
41
  });
package/src/Dir.ts CHANGED
@@ -4,8 +4,9 @@ import sanitizeFilename from 'sanitize-filename';
4
4
  import { File } from './File.ts';
5
5
 
6
6
  /**
7
- * Reference to a specific directory with helpful methods for resolving filepaths,
8
- * sanitizing filenames, and saving files.
7
+ * Reference to a specific directory with methods to create and list files.
8
+ * Created immediately if it doesn't exist.
9
+ * Default path: './'
9
10
  */
10
11
  export class Dir {
11
12
  path;
@@ -13,37 +14,40 @@ export class Dir {
13
14
  /**
14
15
  * @param path can be relative to workspace or absolute
15
16
  */
16
- constructor(_path = './') {
17
- this.path = path.resolve(_path);
17
+ constructor(inputPath = './') {
18
+ this.path = path.resolve(inputPath);
19
+ fs.mkdirSync(this.path, { recursive: true });
18
20
  }
19
21
 
20
- create() {
21
- fs.mkdirSync(this.path, { recursive: true });
22
+ notAbsolute(subPath: string) {
23
+ if (path.isAbsolute(subPath)) throw new Error(`Absolute path provided: "${subPath}"`);
24
+ return subPath;
22
25
  }
23
26
 
24
27
  /**
25
28
  * Create a new Dir inside the current Dir
26
- * @param subPath to create in current Dir
29
+ * @param subPath relative path to create
27
30
  * @example
28
31
  * const folder = new Dir('example');
29
- * // folder.path = './example'
32
+ * // folder.path = '/absolute/path/to/example'
30
33
  * const child = folder.dir('path/to/dir');
31
- * // child.path = './example/path/to/dir'
34
+ * // child.path = '/absolute/path/to/example/path/to/dir'
32
35
  */
33
36
  dir(subPath: string) {
34
- return new Dir(path.resolve(this.path, subPath));
37
+ return new Dir(path.resolve(this.path, this.notAbsolute(subPath)));
35
38
  }
36
39
 
37
40
  tempDir(subPath: string) {
38
- return new TempDir(path.resolve(this.path, subPath));
41
+ return new TempDir(path.resolve(this.path, this.notAbsolute(subPath)));
39
42
  }
40
43
 
41
- sanitize(name: string) {
42
- return sanitizeFilename(name.replace('https://', '').replace('www.', ''), { replacement: '_' }).slice(-200);
44
+ sanitize(filename: string) {
45
+ const notUrl = filename.replace('https://', '').replace('www.', '');
46
+ return sanitizeFilename(notUrl, { replacement: '_' }).slice(-200);
43
47
  }
44
48
 
45
49
  /**
46
- * @param base - The file name with extension
50
+ * @param base - The file base (name and extension)
47
51
  * @example
48
52
  * const folder = new Dir('example');
49
53
  * const filepath = folder.resolve('file.json');
@@ -56,19 +60,33 @@ export class Dir {
56
60
  file(base: string) {
57
61
  return new File(this.filepath(base));
58
62
  }
63
+
64
+ get files() {
65
+ return fs.readdirSync(this.path).map((filename) => this.file(filename));
66
+ }
59
67
  }
60
68
 
61
69
  /**
62
- * Extends Dir class with method to `clear()` contents
70
+ * Extends Dir class with method to `clear()` contents.
71
+ * Default path: `./.${Date.now()}`
63
72
  */
64
73
  export class TempDir extends Dir {
74
+ constructor(inputPath = `./.${Date.now()}`) {
75
+ super(inputPath);
76
+ }
77
+
78
+ /**
79
+ * > ⚠️ Warning! This deletes the directory, make sure it's not
80
+ */
65
81
  clear() {
66
82
  fs.rmSync(this.path, { recursive: true, force: true });
67
- this.create();
83
+ fs.mkdirSync(this.path, { recursive: true });
68
84
  }
69
85
  }
70
86
 
71
87
  /**
72
- * Common temp dir location
88
+ * Creates a '.temp' directory in current working directory
73
89
  */
74
- export const temp = new TempDir('.temp');
90
+ export function temp() {
91
+ return new TempDir('.temp');
92
+ }
package/src/File.test.ts CHANGED
@@ -3,7 +3,7 @@ import assert from 'node:assert';
3
3
  import { temp } from './Dir.ts';
4
4
  import { File } from './File.ts';
5
5
 
6
- const testDir = temp.tempDir('file-test');
6
+ const testDir = temp().tempDir('file-test');
7
7
  testDir.clear();
8
8
 
9
9
  const thing = {
package/src/File.ts CHANGED
@@ -6,7 +6,7 @@ import { writeToStream, parseStream } from 'fast-csv';
6
6
  import { snapshot } from './snapshot.ts';
7
7
 
8
8
  /**
9
- * WARNING: API will change!
9
+ * > ⚠️ WARNING: API will change!
10
10
  */
11
11
  export class File {
12
12
  path;
@@ -0,0 +1,34 @@
1
+ import { describe } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { Format } from './Format.ts';
4
+
5
+ describe('Format.round', () => {
6
+ assert.equal(Format.round(1.22), '1');
7
+ assert.equal(Format.round(1.55), '2');
8
+ assert.equal(Format.round(1.823, 2), '1.82');
9
+ });
10
+
11
+ describe('Format.ms', () => {
12
+ const SECOND = 1000;
13
+ const MINUTE = 60 * SECOND;
14
+ const HOUR = 60 * MINUTE;
15
+ const DAY = 24 * HOUR;
16
+ assert.equal(Format.ms(100), '100ms');
17
+ assert.equal(Format.ms(5 * SECOND), '5s');
18
+ assert.equal(Format.ms(75 * SECOND), '1m 15s');
19
+ assert.equal(Format.ms(30 * MINUTE + 30 * SECOND), '30m 30s');
20
+ assert.equal(Format.ms(2 * HOUR + 2 * MINUTE), '2h 2m');
21
+ assert.equal(Format.ms(2 * DAY + 2 * HOUR), '2d 2h');
22
+ });
23
+
24
+ describe('Format.bytes', () => {
25
+ const base = 1024;
26
+ assert.equal(Format.bytes(2), '2 b');
27
+ assert.equal(Format.bytes(base), '1 KB');
28
+ assert.equal(Format.bytes(base + 0.1 * base), '1.1 KB');
29
+ assert.equal(Format.bytes(base + 0.25 * base), '1.25 KB');
30
+ assert.equal(Format.bytes(base ** 2), '1 MB');
31
+ assert.equal(Format.bytes(base ** 3), '1 GB');
32
+ assert.equal(Format.bytes(base ** 4), '1 TB');
33
+ assert.equal(Format.bytes(base ** 5), '1,024 TB');
34
+ });
package/src/Format.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { format, formatISO, type DateArg } from 'date-fns';
2
+
3
+ /**
4
+ * Helpers for formatting dates, times, and numbers as strings
5
+ */
6
+ export class Format {
7
+ /**
8
+ * date-fns format() with some shortcuts
9
+ * @param formatStr
10
+ * 'iso' to get ISO date, 'ymd' to format as 'yyyy-MM-dd', full options: https://date-fns.org/v4.1.0/docs/format
11
+ */
12
+ static date(formatStr: 'iso' | 'ymd' | string = 'iso', d: DateArg<Date> = new Date()) {
13
+ if (formatStr === 'iso') return formatISO(d);
14
+ if (formatStr === 'ymd') return format(d, 'yyyy-MM-dd');
15
+ return format(d, formatStr);
16
+ }
17
+
18
+ /**
19
+ * Round a number to a specific set of places
20
+ */
21
+ static round(n: number, places = 0) {
22
+ return new Intl.NumberFormat('en-US', { maximumFractionDigits: places }).format(n);
23
+ }
24
+
25
+ /**
26
+ * Make millisecond durations actually readable (eg "123ms", "3.56s", "1m 34s", "3h 24m", "2d 4h")
27
+ */
28
+ static ms(ms: number) {
29
+ if (ms < 1000) return `${this.round(ms)}ms`;
30
+ const s = ms / 1000;
31
+ if (s < 60) return `${this.round(s, 2)}s`;
32
+ const m = Math.floor(s / 60);
33
+ if (m < 60) return `${m}m ${Math.floor(s) % 60}s`;
34
+ const h = Math.floor(m / 60);
35
+ if (h < 24) return `${h}h ${m % 60}m`;
36
+ const d = Math.floor(h / 24);
37
+ return `${d}d ${h % 24}h`;
38
+ }
39
+
40
+ static bytes(b: number) {
41
+ const labels = ['b', 'KB', 'MB', 'GB', 'TB'];
42
+ let factor = 0;
43
+ while (b >= 1024 && labels[factor + 1]) {
44
+ b = b / 1024;
45
+ factor++;
46
+ }
47
+ return `${this.round(b, 2)} ${labels[factor]}`;
48
+ }
49
+ }
package/src/Log.ts CHANGED
@@ -25,9 +25,9 @@ export class Log {
25
25
  */
26
26
  static #toGcloud(entry: Entry) {
27
27
  if (entry.details?.length === 1) {
28
- console.log(JSON.stringify({ ...entry, details: entry.details[0] }));
28
+ console.log(JSON.stringify(snapshot({ ...entry, details: entry.details[0] })));
29
29
  } else {
30
- console.log(JSON.stringify(entry));
30
+ console.log(JSON.stringify(snapshot(entry)));
31
31
  }
32
32
  }
33
33
 
@@ -58,11 +58,12 @@ export class Log {
58
58
 
59
59
  /**
60
60
  * Handle first argument being a string or an object with a 'message' prop
61
- * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
62
61
  */
63
62
  static prepare(...input: unknown[]): { message?: string; details: unknown[] } {
64
- let [first, ...rest] = input.map((i) => snapshot(i));
65
- if (typeof first === 'string') return { message: first, details: rest };
63
+ let [first, ...rest] = input;
64
+ if (typeof first === 'string') {
65
+ return { message: first, details: rest };
66
+ }
66
67
  // @ts-ignore
67
68
  if (isObjectLike(first) && typeof first['message'] === 'string') {
68
69
  const { message, ...firstDetails } = first as { message: string };
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@ export { Dir, TempDir, temp } from './Dir.ts';
2
2
  export { Cache } from './Cache.ts';
3
3
  export { Fetcher, type Route, type Query, type FetchOptions } from './Fetcher.ts';
4
4
  export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.ts';
5
+ export { Format } from './Format.ts';
5
6
  export { Log } from './Log.ts';
6
7
  export { snapshot } from './snapshot.ts';
7
8
  export { timeout } from './timeout.ts';
@@ -46,9 +46,9 @@ describe('snapshot', () => {
46
46
  const t1 = createThing(1);
47
47
  const t2 = createThing(2, t1);
48
48
  const result = snapshot(t1, 20) as Thing;
49
- const f1 = temp.file('recursive').json(result);
49
+ const f1 = temp().file('recursive').json(result);
50
50
  const parsed = f1.read();
51
- const f2 = temp.file('recursive2').json(parsed);
51
+ const f2 = temp().file('recursive2').json(parsed);
52
52
  assert.deepEqual(f1.read(), f2.read());
53
53
  });
54
54
  });
package/tsconfig.json ADDED
@@ -0,0 +1,11 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "strict": true,
7
+ "skipLibCheck": true,
8
+ "allowImportingTsExtensions": true,
9
+ "noEmit": true
10
+ }
11
+ }