@brianbuie/node-kit 0.12.0 → 0.12.2

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
@@ -66,16 +66,15 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
66
66
  ## Class: Dir
67
67
 
68
68
  Reference to a specific directory with methods to create and list files.
69
- Default path: './'
69
+ Default path: '.'
70
70
  > Created on file system the first time .path is read or any methods are used
71
71
 
72
72
  ```ts
73
73
  export class Dir {
74
74
  #inputPath;
75
75
  #resolved?: string;
76
- constructor(inputPath = "./")
76
+ constructor(inputPath = ".")
77
77
  get path()
78
- notAbsolute(subPath: string)
79
78
  dir(subPath: string)
80
79
  tempDir(subPath: string)
81
80
  sanitize(filename: string)
@@ -92,7 +91,7 @@ export class Dir {
92
91
  ### Constructor
93
92
 
94
93
  ```ts
95
- constructor(inputPath = "./")
94
+ constructor(inputPath = ".")
96
95
  ```
97
96
 
98
97
  Argument Details
@@ -111,15 +110,15 @@ dir(subPath: string)
111
110
  Argument Details
112
111
 
113
112
  + **subPath**
114
- + relative path to create (not absolute)
113
+ + joined with parent Dir's path to make new Dir
115
114
 
116
115
  Example
117
116
 
118
117
  ```ts
119
118
  const folder = new Dir('example');
120
- // folder.path = '/absolute/path/to/example'
119
+ // folder.path = '/path/to/cwd/example'
121
120
  const child = folder.dir('path/to/dir');
122
- // child.path = '/absolute/path/to/example/path/to/dir'
121
+ // child.path = '/path/to/cwd/example/path/to/dir'
123
122
  ```
124
123
 
125
124
  ### Method filepath
@@ -152,7 +151,7 @@ tempDir(subPath: string)
152
151
  Argument Details
153
152
 
154
153
  + **subPath**
155
- + relative path to create (not absolute)
154
+ + joined with parent Dir's path to make new TempDir
156
155
 
157
156
  </details>
158
157
 
@@ -259,7 +258,7 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
259
258
  ---
260
259
  ## Class: File
261
260
 
262
- > ⚠️ WARNING: API will change!
261
+ Represents a file on the file system. If the file doesn't exist, it is created the first time it is written to.
263
262
 
264
263
  ```ts
265
264
  export class File {
@@ -267,10 +266,12 @@ export class File {
267
266
  root;
268
267
  dir;
269
268
  base;
270
- ext;
271
269
  name;
270
+ ext;
271
+ type;
272
272
  constructor(filepath: string)
273
273
  get exists()
274
+ get stats(): Partial<fs.Stats>
274
275
  delete()
275
276
  read()
276
277
  lines()
@@ -278,7 +279,6 @@ export class File {
278
279
  get writeStream()
279
280
  write(contents: string | ReadableStream)
280
281
  append(lines: string | string[])
281
- static get FileType()
282
282
  json<T>(contents?: T)
283
283
  static get json()
284
284
  ndjson<T extends object>(lines?: T | T[])
@@ -288,8 +288,6 @@ export class File {
288
288
  }
289
289
  ```
290
290
 
291
- See also: [FileType](#class-filetype)
292
-
293
291
  <details>
294
292
 
295
293
  <summary>Class File Details</summary>
@@ -303,6 +301,56 @@ File always ends with '\n', so contents don't need to be read before appending
303
301
  append(lines: string | string[])
304
302
  ```
305
303
 
304
+ ### Method csv
305
+
306
+ ```ts
307
+ async csv<T extends object>(rows?: T[], keys?: (keyof T)[])
308
+ ```
309
+
310
+ Returns
311
+
312
+ FileTypeCsv adaptor for current File, adds '.csv' extension if not present.
313
+
314
+ Example
315
+
316
+ ```ts
317
+ const file = await new File('a').csv([{ col: 'val' }, { col: 'val2' }]); // FileTypeCsv<{ col: string; }>
318
+ await file.write([ { col2: 'val2' } ]); // ❌ 'col2' doesn't exist on type { col: string; }
319
+ await file.write({ col: 'val' }); // ✅ Writes one row
320
+ await file.write([{ col: 'val2' }, { col: 'val3' }]); // ✅ Writes multiple rows
321
+ ```
322
+
323
+ ### Method delete
324
+
325
+ Deletes the file if it exists
326
+
327
+ ```ts
328
+ delete()
329
+ ```
330
+
331
+ ### Method json
332
+
333
+ ```ts
334
+ json<T>(contents?: T)
335
+ ```
336
+
337
+ Returns
338
+
339
+ FileTypeJson adaptor for current File, adds '.json' extension if not present.
340
+
341
+ Examples
342
+
343
+ ```ts
344
+ const file = new File('./data').json({ key: 'val' }); // FileTypeJson<{ key: string; }>
345
+ console.log(file.path) // '/path/to/cwd/data.json'
346
+ file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
347
+ ```
348
+
349
+ ```ts
350
+ const file = new File('./data').json<object>({ key: 'val' }); // FileTypeJson<object>
351
+ file.write({ something: 'else' }) // ✅ data is typed as object
352
+ ```
353
+
306
354
  ### Method lines
307
355
 
308
356
  ```ts
@@ -313,6 +361,26 @@ Returns
313
361
 
314
362
  lines as strings, removes trailing '\n'
315
363
 
364
+ ### Method ndjson
365
+
366
+ ```ts
367
+ ndjson<T extends object>(lines?: T | T[])
368
+ ```
369
+
370
+ Returns
371
+
372
+ FileTypeNdjson adaptor for current File, adds '.ndjson' extension if not present.
373
+
374
+ ### Method read
375
+
376
+ ```ts
377
+ read()
378
+ ```
379
+
380
+ Returns
381
+
382
+ the contents of the file as a string, or undefined if the file doesn't exist
383
+
316
384
  </details>
317
385
 
318
386
  Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
@@ -357,7 +425,20 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
357
425
  ## Class: FileTypeJson
358
426
 
359
427
  A .json file that maintains data type when reading/writing.
360
- This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
428
+ > ⚠️ This is mildly unsafe, important/foreign json files should be validated at runtime!
429
+
430
+ Examples
431
+
432
+ ```ts
433
+ const file = new FileTypeJson('./data', { key: 'val' }); // FileTypeJson<{ key: string; }>
434
+ console.log(file.path) // '/path/to/cwd/data.json'
435
+ file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
436
+ ```
437
+
438
+ ```ts
439
+ const file = new FileTypeJson<object>('./data', { key: 'val' }); // FileTypeJson<object>
440
+ file.write({ something: 'else' }) // ✅ data is typed as object
441
+ ```
361
442
 
362
443
  ```ts
363
444
  export class FileTypeJson<T> extends FileType {
package/dist/index.d.mts CHANGED
@@ -15,18 +15,26 @@ 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
+ * Represents a file on the file system. If the file doesn't exist, it is created the first time it is written to.
19
19
  */
20
20
  declare class File {
21
21
  path: string;
22
22
  root: string;
23
23
  dir: string;
24
24
  base: string;
25
- ext: string;
26
25
  name: string;
26
+ ext: string;
27
+ type: string | undefined;
27
28
  constructor(filepath: string);
28
29
  get exists(): boolean;
30
+ get stats(): Partial<fs.Stats>;
31
+ /**
32
+ * Deletes the file if it exists
33
+ */
29
34
  delete(): void;
35
+ /**
36
+ * @returns the contents of the file as a string, or undefined if the file doesn't exist
37
+ */
30
38
  read(): string | undefined;
31
39
  /**
32
40
  * @returns lines as strings, removes trailing '\n'
@@ -40,11 +48,40 @@ declare class File {
40
48
  * File always ends with '\n', so contents don't need to be read before appending
41
49
  */
42
50
  append(lines: string | string[]): void;
43
- static get FileType(): typeof FileType;
51
+ /**
52
+ * @returns FileTypeJson adaptor for current File, adds '.json' extension if not present.
53
+ * @example
54
+ * const file = new File('./data').json({ key: 'val' }); // FileTypeJson<{ key: string; }>
55
+ * console.log(file.path) // '/path/to/cwd/data.json'
56
+ * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
57
+ * @example
58
+ * const file = new File('./data').json<object>({ key: 'val' }); // FileTypeJson<object>
59
+ * file.write({ something: 'else' }) // ✅ data is typed as object
60
+ */
44
61
  json<T>(contents?: T): FileTypeJson<T>;
62
+ /**
63
+ * @example
64
+ * const file = new File.json('data.json', { key: 'val' }); // FileTypeJson<{ key: string; }>
65
+ */
45
66
  static get json(): typeof FileTypeJson;
67
+ /**
68
+ * @returns FileTypeNdjson adaptor for current File, adds '.ndjson' extension if not present.
69
+ */
46
70
  ndjson<T extends object>(lines?: T | T[]): FileTypeNdjson<T>;
71
+ /**
72
+ * @example
73
+ * const file = new File.ndjson('log', { key: 'val' }); // FileTypeNdjson<{ key: string; }>
74
+ * console.log(file.path) // /path/to/cwd/log.ndjson
75
+ */
47
76
  static get ndjson(): typeof FileTypeNdjson;
77
+ /**
78
+ * @returns FileTypeCsv adaptor for current File, adds '.csv' extension if not present.
79
+ * @example
80
+ * const file = await new File('a').csv([{ col: 'val' }, { col: 'val2' }]); // FileTypeCsv<{ col: string; }>
81
+ * await file.write([ { col2: 'val2' } ]); // ❌ 'col2' doesn't exist on type { col: string; }
82
+ * await file.write({ col: 'val' }); // ✅ Writes one row
83
+ * await file.write([{ col: 'val2' }, { col: 'val3' }]); // ✅ Writes multiple rows
84
+ */
48
85
  csv<T extends object>(rows?: T[], keys?: (keyof T)[]): Promise<FileTypeCsv<T>>;
49
86
  static get csv(): typeof FileTypeCsv;
50
87
  }
@@ -60,7 +97,14 @@ declare class FileType {
60
97
  }
61
98
  /**
62
99
  * A .json file that maintains data type when reading/writing.
63
- * This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
100
+ * > ⚠️ This is mildly unsafe, important/foreign json files should be validated at runtime!
101
+ * @example
102
+ * const file = new FileTypeJson('./data', { key: 'val' }); // FileTypeJson<{ key: string; }>
103
+ * console.log(file.path) // '/path/to/cwd/data.json'
104
+ * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
105
+ * @example
106
+ * const file = new FileTypeJson<object>('./data', { key: 'val' }); // FileTypeJson<object>
107
+ * file.write({ something: 'else' }) // ✅ data is typed as object
64
108
  */
65
109
  declare class FileTypeJson<T> extends FileType {
66
110
  constructor(filepath: string, contents?: T);
@@ -91,7 +135,7 @@ declare class FileTypeCsv<Row extends object> extends FileType {
91
135
  //#region src/Dir.d.ts
92
136
  /**
93
137
  * Reference to a specific directory with methods to create and list files.
94
- * Default path: './'
138
+ * Default path: '.'
95
139
  * > Created on file system the first time .path is read or any methods are used
96
140
  */
97
141
  declare class Dir {
@@ -104,20 +148,19 @@ declare class Dir {
104
148
  * The path of this Dir instance. Created on file system the first time this property is read/used.
105
149
  */
106
150
  get path(): string;
107
- notAbsolute(subPath: string): string;
108
151
  /**
109
152
  * Create a new Dir inside the current Dir
110
- * @param subPath relative path to create (not absolute)
153
+ * @param subPath joined with parent Dir's path to make new Dir
111
154
  * @example
112
155
  * const folder = new Dir('example');
113
- * // folder.path = '/absolute/path/to/example'
156
+ * // folder.path = '/path/to/cwd/example'
114
157
  * const child = folder.dir('path/to/dir');
115
- * // child.path = '/absolute/path/to/example/path/to/dir'
158
+ * // child.path = '/path/to/cwd/example/path/to/dir'
116
159
  */
117
160
  dir(subPath: string): Dir;
118
161
  /**
119
162
  * Creates a new TempDir inside current Dir
120
- * @param subPath relative path to create (not absolute)
163
+ * @param subPath joined with parent Dir's path to make new TempDir
121
164
  */
122
165
  tempDir(subPath: string): TempDir;
123
166
  sanitize(filename: string): string;
@@ -1 +1 @@
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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"}
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
@@ -3,6 +3,7 @@ import * as path from "node:path";
3
3
  import sanitizeFilename from "sanitize-filename";
4
4
  import { Readable } from "node:stream";
5
5
  import { finished } from "node:stream/promises";
6
+ import mime from "mime-types";
6
7
  import { parseStream, writeToStream } from "fast-csv";
7
8
  import { isObjectLike, merge } from "lodash-es";
8
9
  import { add, format, formatISO, isAfter } from "date-fns";
@@ -40,30 +41,41 @@ function snapshot(i, max = 50, depth = 0) {
40
41
  //#endregion
41
42
  //#region src/File.ts
42
43
  /**
43
- * > ⚠️ WARNING: API will change!
44
+ * Represents a file on the file system. If the file doesn't exist, it is created the first time it is written to.
44
45
  */
45
46
  var File = class {
46
47
  path;
47
48
  root;
48
49
  dir;
49
50
  base;
50
- ext;
51
51
  name;
52
+ ext;
53
+ type;
52
54
  constructor(filepath) {
53
- this.path = filepath;
54
- const { root, dir, base, ext, name } = path.parse(filepath);
55
+ this.path = path.resolve(filepath);
56
+ const { root, dir, base, ext, name } = path.parse(this.path);
55
57
  this.root = root;
56
58
  this.dir = dir;
57
59
  this.base = base;
58
- this.ext = ext;
59
60
  this.name = name;
61
+ this.ext = ext;
62
+ this.type = mime.lookup(ext) || void 0;
60
63
  }
61
64
  get exists() {
62
65
  return fs.existsSync(this.path);
63
66
  }
67
+ get stats() {
68
+ return this.exists ? fs.statSync(this.path) : {};
69
+ }
70
+ /**
71
+ * Deletes the file if it exists
72
+ */
64
73
  delete() {
65
74
  fs.rmSync(this.path, { force: true });
66
75
  }
76
+ /**
77
+ * @returns the contents of the file as a string, or undefined if the file doesn't exist
78
+ */
67
79
  read() {
68
80
  return this.exists ? fs.readFileSync(this.path, "utf8") : void 0;
69
81
  }
@@ -72,17 +84,17 @@ var File = class {
72
84
  */
73
85
  lines() {
74
86
  const contents = (this.read() || "").split("\n");
75
- return contents.slice(0, contents.length - 1);
87
+ return contents.at(-1)?.length ? contents : contents.slice(0, contents.length - 1);
76
88
  }
77
89
  get readStream() {
78
90
  return this.exists ? fs.createReadStream(this.path) : Readable.from([]);
79
91
  }
80
92
  get writeStream() {
81
- fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
93
+ fs.mkdirSync(this.dir, { recursive: true });
82
94
  return fs.createWriteStream(this.path);
83
95
  }
84
96
  write(contents) {
85
- fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
97
+ fs.mkdirSync(this.dir, { recursive: true });
86
98
  if (typeof contents === "string") return fs.writeFileSync(this.path, contents);
87
99
  if (contents instanceof ReadableStream) return finished(Readable.from(contents).pipe(this.writeStream));
88
100
  throw new Error(`Invalid content type: ${typeof contents}`);
@@ -96,21 +108,48 @@ var File = class {
96
108
  const contents = Array.isArray(lines) ? lines.join("\n") : lines;
97
109
  fs.appendFileSync(this.path, contents + "\n");
98
110
  }
99
- static get FileType() {
100
- return FileType;
101
- }
111
+ /**
112
+ * @returns FileTypeJson adaptor for current File, adds '.json' extension if not present.
113
+ * @example
114
+ * const file = new File('./data').json({ key: 'val' }); // FileTypeJson<{ key: string; }>
115
+ * console.log(file.path) // '/path/to/cwd/data.json'
116
+ * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
117
+ * @example
118
+ * const file = new File('./data').json<object>({ key: 'val' }); // FileTypeJson<object>
119
+ * file.write({ something: 'else' }) // ✅ data is typed as object
120
+ */
102
121
  json(contents) {
103
122
  return new FileTypeJson(this.path, contents);
104
123
  }
124
+ /**
125
+ * @example
126
+ * const file = new File.json('data.json', { key: 'val' }); // FileTypeJson<{ key: string; }>
127
+ */
105
128
  static get json() {
106
129
  return FileTypeJson;
107
130
  }
131
+ /**
132
+ * @returns FileTypeNdjson adaptor for current File, adds '.ndjson' extension if not present.
133
+ */
108
134
  ndjson(lines) {
109
135
  return new FileTypeNdjson(this.path, lines);
110
136
  }
137
+ /**
138
+ * @example
139
+ * const file = new File.ndjson('log', { key: 'val' }); // FileTypeNdjson<{ key: string; }>
140
+ * console.log(file.path) // /path/to/cwd/log.ndjson
141
+ */
111
142
  static get ndjson() {
112
143
  return FileTypeNdjson;
113
144
  }
145
+ /**
146
+ * @returns FileTypeCsv adaptor for current File, adds '.csv' extension if not present.
147
+ * @example
148
+ * const file = await new File('a').csv([{ col: 'val' }, { col: 'val2' }]); // FileTypeCsv<{ col: string; }>
149
+ * await file.write([ { col2: 'val2' } ]); // ❌ 'col2' doesn't exist on type { col: string; }
150
+ * await file.write({ col: 'val' }); // ✅ Writes one row
151
+ * await file.write([{ col: 'val2' }, { col: 'val3' }]); // ✅ Writes multiple rows
152
+ */
114
153
  async csv(rows, keys) {
115
154
  const csvFile = new FileTypeCsv(this.path);
116
155
  if (rows) await csvFile.write(rows, keys);
@@ -141,7 +180,14 @@ var FileType = class {
141
180
  };
142
181
  /**
143
182
  * A .json file that maintains data type when reading/writing.
144
- * This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
183
+ * > ⚠️ This is mildly unsafe, important/foreign json files should be validated at runtime!
184
+ * @example
185
+ * const file = new FileTypeJson('./data', { key: 'val' }); // FileTypeJson<{ key: string; }>
186
+ * console.log(file.path) // '/path/to/cwd/data.json'
187
+ * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
188
+ * @example
189
+ * const file = new FileTypeJson<object>('./data', { key: 'val' }); // FileTypeJson<object>
190
+ * file.write({ something: 'else' }) // ✅ data is typed as object
145
191
  */
146
192
  var FileTypeJson = class extends FileType {
147
193
  constructor(filepath, contents) {
@@ -198,12 +244,12 @@ var FileTypeCsv = class extends FileType {
198
244
  async read() {
199
245
  return new Promise((resolve, reject) => {
200
246
  const parsed = [];
201
- parseStream(this.file.readStream, { headers: true }).on("error", (e) => reject(e)).on("data", (raw) => {
247
+ parseStream(this.file.readStream, { headers: true }).on("data", (raw) => {
202
248
  parsed.push(Object.entries(raw).reduce((all, [key, val]) => ({
203
249
  ...all,
204
250
  [key]: this.#parseVal(val)
205
251
  }), {}));
206
- }).on("end", () => resolve(parsed));
252
+ }).on("error", (e) => reject(e)).on("end", () => resolve(parsed));
207
253
  });
208
254
  }
209
255
  };
@@ -212,7 +258,7 @@ var FileTypeCsv = class extends FileType {
212
258
  //#region src/Dir.ts
213
259
  /**
214
260
  * Reference to a specific directory with methods to create and list files.
215
- * Default path: './'
261
+ * Default path: '.'
216
262
  * > Created on file system the first time .path is read or any methods are used
217
263
  */
218
264
  var Dir = class Dir {
@@ -221,8 +267,8 @@ var Dir = class Dir {
221
267
  /**
222
268
  * @param path can be relative to workspace or absolute
223
269
  */
224
- constructor(inputPath = "./") {
225
- this.#inputPath = path.resolve(inputPath);
270
+ constructor(inputPath = ".") {
271
+ this.#inputPath = inputPath;
226
272
  }
227
273
  /**
228
274
  * The path of this Dir instance. Created on file system the first time this property is read/used.
@@ -234,28 +280,24 @@ var Dir = class Dir {
234
280
  }
235
281
  return this.#resolved;
236
282
  }
237
- notAbsolute(subPath) {
238
- if (path.isAbsolute(subPath)) throw new Error(`Absolute path provided: "${subPath}"`);
239
- return subPath;
240
- }
241
283
  /**
242
284
  * Create a new Dir inside the current Dir
243
- * @param subPath relative path to create (not absolute)
285
+ * @param subPath joined with parent Dir's path to make new Dir
244
286
  * @example
245
287
  * const folder = new Dir('example');
246
- * // folder.path = '/absolute/path/to/example'
288
+ * // folder.path = '/path/to/cwd/example'
247
289
  * const child = folder.dir('path/to/dir');
248
- * // child.path = '/absolute/path/to/example/path/to/dir'
290
+ * // child.path = '/path/to/cwd/example/path/to/dir'
249
291
  */
250
292
  dir(subPath) {
251
- return new Dir(path.resolve(this.path, this.notAbsolute(subPath)));
293
+ return new Dir(path.join(this.path, subPath));
252
294
  }
253
295
  /**
254
296
  * Creates a new TempDir inside current Dir
255
- * @param subPath relative path to create (not absolute)
297
+ * @param subPath joined with parent Dir's path to make new TempDir
256
298
  */
257
299
  tempDir(subPath) {
258
- return new TempDir(path.resolve(this.path, this.notAbsolute(subPath)));
300
+ return new TempDir(path.join(this.path, subPath));
259
301
  }
260
302
  sanitize(filename) {
261
303
  return sanitizeFilename(filename.replace("https://", "").replace("www.", ""), { replacement: "_" }).slice(-200);
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["output: Record<string, any>","obj: Record<string, any>","parsed: Row[]","#parseVal","#inputPath","#resolved","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 * Default path: './'\n * > Created on file system the first time .path is read or any methods are used\n */\nexport class Dir {\n #inputPath;\n #resolved?: string;\n\n /**\n * @param path can be relative to workspace or absolute\n */\n constructor(inputPath = './') {\n this.#inputPath = path.resolve(inputPath);\n }\n\n /**\n * The path of this Dir instance. Created on file system the first time this property is read/used.\n */\n get path() {\n if (!this.#resolved) {\n this.#resolved = path.resolve(this.#inputPath);\n fs.mkdirSync(this.#resolved, { recursive: true });\n }\n return this.#resolved;\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 (not absolute)\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 /**\n * Creates a new TempDir inside current Dir\n * @param subPath relative path to create (not absolute)\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: `./.temp`\n */\nexport class TempDir extends Dir {\n constructor(inputPath = `./.temp`) {\n super(inputPath);\n }\n\n /**\n * > ⚠️ Warning! This deletes the directory!\n */\n clear() {\n fs.rmSync(this.path, { recursive: true, force: true });\n fs.mkdirSync(this.path, { recursive: true });\n }\n}\n\n/**\n * './.temp' in current working directory\n */\nexport const temp = new TempDir();\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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"}
1
+ {"version":3,"file":"index.mjs","names":["output: Record<string, any>","obj: Record<string, any>","parsed: Row[]","#parseVal","#inputPath","#resolved","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 mime from 'mime-types';\nimport { writeToStream, parseStream } from 'fast-csv';\nimport { snapshot } from './snapshot.ts';\n\n/**\n * Represents a file on the file system. If the file doesn't exist, it is created the first time it is written to.\n */\nexport class File {\n path;\n root;\n dir;\n base;\n name;\n ext;\n type;\n\n constructor(filepath: string) {\n this.path = path.resolve(filepath);\n const { root, dir, base, ext, name } = path.parse(this.path);\n this.root = root;\n this.dir = dir;\n this.base = base;\n this.name = name;\n this.ext = ext;\n this.type = mime.lookup(ext) || undefined;\n }\n\n get exists() {\n return fs.existsSync(this.path);\n }\n\n get stats(): Partial<fs.Stats> {\n return this.exists ? fs.statSync(this.path) : {};\n }\n\n /**\n * Deletes the file if it exists\n */\n delete() {\n fs.rmSync(this.path, { force: true });\n }\n\n /**\n * @returns the contents of the file as a string, or undefined if the file doesn't exist\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.at(-1)?.length ? contents : 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(this.dir, { recursive: true });\n return fs.createWriteStream(this.path);\n }\n\n write(contents: string | ReadableStream) {\n fs.mkdirSync(this.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 /**\n * @returns FileTypeJson adaptor for current File, adds '.json' extension if not present.\n * @example\n * const file = new File('./data').json({ key: 'val' }); // FileTypeJson<{ key: string; }>\n * console.log(file.path) // '/path/to/cwd/data.json'\n * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }\n * @example\n * const file = new File('./data').json<object>({ key: 'val' }); // FileTypeJson<object>\n * file.write({ something: 'else' }) // ✅ data is typed as object\n */\n json<T>(contents?: T) {\n return new FileTypeJson<T>(this.path, contents);\n }\n\n /**\n * @example\n * const file = new File.json('data.json', { key: 'val' }); // FileTypeJson<{ key: string; }>\n */\n static get json() {\n return FileTypeJson;\n }\n\n /**\n * @returns FileTypeNdjson adaptor for current File, adds '.ndjson' extension if not present.\n */\n ndjson<T extends object>(lines?: T | T[]) {\n return new FileTypeNdjson<T>(this.path, lines);\n }\n /**\n * @example\n * const file = new File.ndjson('log', { key: 'val' }); // FileTypeNdjson<{ key: string; }>\n * console.log(file.path) // /path/to/cwd/log.ndjson\n */\n static get ndjson() {\n return FileTypeNdjson;\n }\n\n /**\n * @returns FileTypeCsv adaptor for current File, adds '.csv' extension if not present.\n * @example\n * const file = await new File('a').csv([{ col: 'val' }, { col: 'val2' }]); // FileTypeCsv<{ col: string; }>\n * await file.write([ { col2: 'val2' } ]); // ❌ 'col2' doesn't exist on type { col: string; }\n * await file.write({ col: 'val' }); // ✅ Writes one row\n * await file.write([{ col: 'val2' }, { col: 'val3' }]); // ✅ Writes multiple rows\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 mildly unsafe, important/foreign json files should be validated at runtime!\n * @example\n * const file = new FileTypeJson('./data', { key: 'val' }); // FileTypeJson<{ key: string; }>\n * console.log(file.path) // '/path/to/cwd/data.json'\n * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }\n * @example\n * const file = new FileTypeJson<object>('./data', { key: 'val' }); // FileTypeJson<object>\n * file.write({ something: 'else' }) // ✅ data is typed as object\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('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('error', (e) => reject(e))\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 * Default path: '.'\n * > Created on file system the first time .path is read or any methods are used\n */\nexport class Dir {\n #inputPath;\n #resolved?: string;\n\n /**\n * @param path can be relative to workspace or absolute\n */\n constructor(inputPath = '.') {\n this.#inputPath = inputPath;\n }\n\n /**\n * The path of this Dir instance. Created on file system the first time this property is read/used.\n */\n get path() {\n if (!this.#resolved) {\n this.#resolved = path.resolve(this.#inputPath);\n fs.mkdirSync(this.#resolved, { recursive: true });\n }\n return this.#resolved;\n }\n\n /**\n * Create a new Dir inside the current Dir\n * @param subPath joined with parent Dir's path to make new Dir\n * @example\n * const folder = new Dir('example');\n * // folder.path = '/path/to/cwd/example'\n * const child = folder.dir('path/to/dir');\n * // child.path = '/path/to/cwd/example/path/to/dir'\n */\n dir(subPath: string) {\n return new Dir(path.join(this.path, subPath));\n }\n\n /**\n * Creates a new TempDir inside current Dir\n * @param subPath joined with parent Dir's path to make new TempDir\n */\n tempDir(subPath: string) {\n return new TempDir(path.join(this.path, 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: `./.temp`\n */\nexport class TempDir extends Dir {\n constructor(inputPath = `./.temp`) {\n super(inputPath);\n }\n\n /**\n * > ⚠️ Warning! This deletes the directory!\n */\n clear() {\n fs.rmSync(this.path, { recursive: true, force: true });\n fs.mkdirSync(this.path, { recursive: true });\n }\n}\n\n/**\n * './.temp' in current working directory\n */\nexport const temp = new TempDir();\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":";;;;;;;;;;;;;;;;;;;AAMA,SAAgB,SAAS,GAAY,MAAM,IAAI,QAAQ,GAAQ;AAC7D,KAAI,MAAM,QAAQ,EAAE,EAAE;AACpB,MAAI,UAAU,IAAK,QAAO,EAAE;AAC5B,SAAO,EAAE,KAAK,MAAM,SAAS,GAAG,KAAK,QAAQ,EAAE,CAAC;;AAElD,KAAI,OAAO,MAAM,WAAY,QAAO;AACpC,KAAI,CAAC,aAAa,EAAE,CAAE,QAAO;AAE7B,KAAI,UAAU,IAAK,QAAO,EAAE;CAC5B,IAAIA,SAA8B,EAAE;AAEpC,KAAI,OAAO,EAAE,YAAY,YAAY;AAEnC,OAAK,IAAI,CAAC,GAAG,MAAM,EAAE,SAAS,CAC5B,QAAO,KAAK,SAAS,GAAG,KAAK,QAAQ,EAAE;AAEzC,SAAO;;CAMT,MAAMC,MAA2B;AACjC,MAAK,IAAI,OAAO,IACd,QAAO,OAAO,SAAS,IAAI,MAAM,KAAK,QAAQ,EAAE;AAIlD,QAAO,oBAAoB,IAAI,CAAC,SAAS,QAAQ;AAC/C,SAAO,OAAO,SAAS,IAAI,MAAM,KAAK,QAAQ,EAAE;GAChD;AAEF,QAAO;;;;;;;;AC3BT,IAAa,OAAb,MAAkB;CAChB;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,UAAkB;AAC5B,OAAK,OAAO,KAAK,QAAQ,SAAS;EAClC,MAAM,EAAE,MAAM,KAAK,MAAM,KAAK,SAAS,KAAK,MAAM,KAAK,KAAK;AAC5D,OAAK,OAAO;AACZ,OAAK,MAAM;AACX,OAAK,OAAO;AACZ,OAAK,OAAO;AACZ,OAAK,MAAM;AACX,OAAK,OAAO,KAAK,OAAO,IAAI,IAAI;;CAGlC,IAAI,SAAS;AACX,SAAO,GAAG,WAAW,KAAK,KAAK;;CAGjC,IAAI,QAA2B;AAC7B,SAAO,KAAK,SAAS,GAAG,SAAS,KAAK,KAAK,GAAG,EAAE;;;;;CAMlD,SAAS;AACP,KAAG,OAAO,KAAK,MAAM,EAAE,OAAO,MAAM,CAAC;;;;;CAMvC,OAAO;AACL,SAAO,KAAK,SAAS,GAAG,aAAa,KAAK,MAAM,OAAO,GAAG;;;;;CAM5D,QAAQ;EACN,MAAM,YAAY,KAAK,MAAM,IAAI,IAAI,MAAM,KAAK;AAChD,SAAO,SAAS,GAAG,GAAG,EAAE,SAAS,WAAW,SAAS,MAAM,GAAG,SAAS,SAAS,EAAE;;CAGpF,IAAI,aAAa;AACf,SAAO,KAAK,SAAS,GAAG,iBAAiB,KAAK,KAAK,GAAG,SAAS,KAAK,EAAE,CAAC;;CAGzE,IAAI,cAAc;AAChB,KAAG,UAAU,KAAK,KAAK,EAAE,WAAW,MAAM,CAAC;AAC3C,SAAO,GAAG,kBAAkB,KAAK,KAAK;;CAGxC,MAAM,UAAmC;AACvC,KAAG,UAAU,KAAK,KAAK,EAAE,WAAW,MAAM,CAAC;AAC3C,MAAI,OAAO,aAAa,SAAU,QAAO,GAAG,cAAc,KAAK,MAAM,SAAS;AAC9E,MAAI,oBAAoB,eAAgB,QAAO,SAAS,SAAS,KAAK,SAAS,CAAC,KAAK,KAAK,YAAY,CAAC;AACvG,QAAM,IAAI,MAAM,yBAAyB,OAAO,WAAW;;;;;;CAO7D,OAAO,OAA0B;AAC/B,MAAI,CAAC,KAAK,OAAQ,MAAK,MAAM,GAAG;EAChC,MAAM,WAAW,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,KAAK,GAAG;AAC3D,KAAG,eAAe,KAAK,MAAM,WAAW,KAAK;;;;;;;;;;;;CAa/C,KAAQ,UAAc;AACpB,SAAO,IAAI,aAAgB,KAAK,MAAM,SAAS;;;;;;CAOjD,WAAW,OAAO;AAChB,SAAO;;;;;CAMT,OAAyB,OAAiB;AACxC,SAAO,IAAI,eAAkB,KAAK,MAAM,MAAM;;;;;;;CAOhD,WAAW,SAAS;AAClB,SAAO;;;;;;;;;;CAWT,MAAM,IAAsB,MAAY,MAAoB;EAC1D,MAAM,UAAU,IAAI,YAAe,KAAK,KAAK;AAC7C,MAAI,KAAM,OAAM,QAAQ,MAAM,MAAM,KAAK;AACzC,SAAO;;CAGT,WAAW,MAAM;AACf,SAAO;;;;;;AAOX,IAAa,WAAb,MAAsB;CACpB;CAEA,YAAY,UAAkB,UAAmB;AAC/C,OAAK,OAAO,IAAI,KAAK,SAAS;AAC9B,MAAI,SAAU,MAAK,KAAK,MAAM,SAAS;;CAGzC,IAAI,SAAS;AACX,SAAO,KAAK,KAAK;;CAGnB,IAAI,OAAO;AACT,SAAO,KAAK,KAAK;;CAGnB,SAAS;AACP,OAAK,KAAK,QAAQ;;;;;;;;;;;;;;AAetB,IAAa,eAAb,cAAqC,SAAS;CAC5C,YAAY,UAAkB,UAAc;AAC1C,QAAM,SAAS,SAAS,QAAQ,GAAG,WAAW,WAAW,QAAQ;AACjE,MAAI,SAAU,MAAK,MAAM,SAAS;;CAGpC,OAAO;EACL,MAAM,WAAW,KAAK,KAAK,MAAM;AACjC,SAAO,WAAY,KAAK,MAAM,SAAS,GAAS;;CAGlD,MAAM,UAAa;AACjB,OAAK,KAAK,MAAM,KAAK,UAAU,SAAS,SAAS,EAAE,MAAM,EAAE,CAAC;;;;;;;AAQhE,IAAa,iBAAb,cAAsD,SAAS;CAC7D,YAAY,UAAkB,OAAiB;AAC7C,QAAM,SAAS,SAAS,UAAU,GAAG,WAAW,WAAW,UAAU;AACrE,MAAI,MAAO,MAAK,OAAO,MAAM;;CAG/B,OAAO,OAAgB;AACrB,OAAK,KAAK,OACR,MAAM,QAAQ,MAAM,GAAG,MAAM,KAAK,MAAM,KAAK,UAAU,SAAS,EAAE,CAAC,CAAC,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CACvG;;CAGH,QAAQ;AACN,SAAO,KAAK,KAAK,OAAO,CAAC,KAAK,MAAM,KAAK,MAAM,EAAE,CAAM;;;;;;;AAU3D,IAAa,cAAb,cAAqD,SAAS;CAC5D,YAAY,UAAkB;AAC5B,QAAM,SAAS,SAAS,OAAO,GAAG,WAAW,WAAW,OAAO;;CAGjE,MAAM,MAAM,MAAa,MAAmB;EAC1C,MAAM,4BAAY,IAAI,KAAe;AACrC,MAAI,KACF,MAAK,MAAM,OAAO,KAAM,WAAU,IAAI,IAAI;MAE1C,MAAK,MAAM,OAAO,KAChB,MAAK,MAAM,OAAO,IAAK,WAAU,IAAI,IAAI;EAG7C,MAAM,UAAU,MAAM,KAAK,UAAU;EACrC,MAAM,UAAU,KAAK,KAAK,QAAQ,QAAQ,KAAK,QAAQ,IAAI,KAAK,CAAC;AACjE,SAAO,SAAS,cAAc,KAAK,KAAK,aAAa,CAAC,SAAS,GAAG,QAAQ,CAAC,CAAC;;CAG9E,UAAU,KAAa;AACrB,MAAI,IAAI,aAAa,KAAK,QAAS,QAAO;AAC1C,MAAI,IAAI,aAAa,KAAK,OAAQ,QAAO;AACzC,MAAI,IAAI,WAAW,EAAG,QAAO;AAC7B,MAAI,aAAa,KAAK,IAAI,CAAE,QAAO,OAAO,IAAI;AAC9C,SAAO;;CAGT,MAAM,OAAO;AACX,SAAO,IAAI,SAAgB,SAAS,WAAW;GAC7C,MAAMC,SAAgB,EAAE;AACxB,eAAY,KAAK,KAAK,YAAY,EAAE,SAAS,MAAM,CAAC,CACjD,GAAG,SAAS,QAAkC;AAC7C,WAAO,KACL,OAAO,QAAQ,IAAI,CAAC,QACjB,KAAK,CAAC,KAAK,UAAU;KACpB,GAAG;MACF,MAAM,MAAKC,SAAU,IAAc;KACrC,GACD,EAAE,CACH,CACF;KACD,CACD,GAAG,UAAU,MAAM,OAAO,EAAE,CAAC,CAC7B,GAAG,aAAa,QAAQ,OAAO,CAAC;IACnC;;;;;;;;;;;AC/PN,IAAa,MAAb,MAAa,IAAI;CACf;CACA;;;;CAKA,YAAY,YAAY,KAAK;AAC3B,QAAKC,YAAa;;;;;CAMpB,IAAI,OAAO;AACT,MAAI,CAAC,MAAKC,UAAW;AACnB,SAAKA,WAAY,KAAK,QAAQ,MAAKD,UAAW;AAC9C,MAAG,UAAU,MAAKC,UAAW,EAAE,WAAW,MAAM,CAAC;;AAEnD,SAAO,MAAKA;;;;;;;;;;;CAYd,IAAI,SAAiB;AACnB,SAAO,IAAI,IAAI,KAAK,KAAK,KAAK,MAAM,QAAQ,CAAC;;;;;;CAO/C,QAAQ,SAAiB;AACvB,SAAO,IAAI,QAAQ,KAAK,KAAK,KAAK,MAAM,QAAQ,CAAC;;CAGnD,SAAS,UAAkB;AAEzB,SAAO,iBADQ,SAAS,QAAQ,YAAY,GAAG,CAAC,QAAQ,QAAQ,GAAG,EACnC,EAAE,aAAa,KAAK,CAAC,CAAC,MAAM,KAAK;;;;;;;;;CAUnE,SAAS,MAAc;AACrB,SAAO,KAAK,QAAQ,KAAK,MAAM,KAAK,SAAS,KAAK,CAAC;;CAGrD,KAAK,MAAc;AACjB,SAAO,IAAI,KAAK,KAAK,SAAS,KAAK,CAAC;;CAGtC,IAAI,QAAQ;AACV,SAAO,GAAG,YAAY,KAAK,KAAK,CAAC,KAAK,aAAa,KAAK,KAAK,SAAS,CAAC;;;;;;;AAQ3E,IAAa,UAAb,cAA6B,IAAI;CAC/B,YAAY,YAAY,WAAW;AACjC,QAAM,UAAU;;;;;CAMlB,QAAQ;AACN,KAAG,OAAO,KAAK,MAAM;GAAE,WAAW;GAAM,OAAO;GAAM,CAAC;AACtD,KAAG,UAAU,KAAK,MAAM,EAAE,WAAW,MAAM,CAAC;;;;;;AAOhD,MAAa,OAAO,IAAI,SAAS;;;;;;;;;AC3FjC,IAAa,QAAb,MAAsB;CACpB;CACA;CAEA,YAAY,KAAa,KAAwB,aAAiB;AAEhE,OAAK,OADO,IAAI,QAAQ,SAAS,CACjB,KAAK,IAAI,CAAC,MAAoC;AAC9D,OAAK,MAAM,OAAO,QAAQ,WAAW,EAAE,SAAS,KAAK,GAAG;AACxD,MAAI,YAAa,MAAK,MAAM,YAAY;;CAG1C,MAAM,MAAS;AACb,OAAK,KAAK,MAAM;GAAE,0BAAS,IAAI,MAAM,EAAC,aAAa;GAAE;GAAM,CAAC;;CAG9D,OAAiC;EAC/B,MAAM,EAAE,SAAS,SAAS,KAAK,KAAK,MAAM,IAAI,EAAE;AAEhD,SAAO,CAAC,MADQ,QAAQ,WAAW,QAAQ,IAAI,SAAS,KAAK,IAAI,kBAAE,IAAI,MAAM,CAAC,CAAC,CACzD;;;;;;;;;;;ACH1B,IAAa,UAAb,MAAqB;CACnB;CAEA,YAAY,OAAqB,EAAE,EAAE;AACnC,OAAK,iBAAiB;GACpB,SAAS;GACT,SAAS;GACT,YAAY;GACZ,GAAG;GACJ;;;;;;CAOH,SAAS,OAAc,OAAqB,EAAE,EAAiB;EAC7D,MAAM,gBAAgB,MAAM,EAAE,EAAE,KAAK,gBAAgB,KAAK;EAC1D,MAAMC,SAA6B,EAAE;AACrC,SAAO,QAAQ,cAAc,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,KAAK,SAAS;AAChE,OAAI,QAAQ,OAAW;AACvB,OAAI,MAAM,QAAQ,IAAI,CACpB,KAAI,SAAS,MAAM;AACjB,WAAO,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;KAC1B;OAEF,QAAO,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC;IAE9B;EACF,MAAM,SAAS,OAAO,SAAS,IAAI,MAAM,IAAI,gBAAgB,OAAO,CAAC,UAAU,GAAG;EAClF,MAAM,MAAM,IAAI,IAAI,QAAQ,QAAQ,KAAK,eAAe,KAAK;AAE7D,SAAO,CAAC,KADO,cAAc,IAAI,KAAK,CAClB;;;;;CAMtB,aAAa,OAAc,OAAqB,EAAE,EAAE;EAClD,MAAM,EAAE,YAAY,MAAM,EAAE,EAAE,KAAK,gBAAgB,KAAK;AACxD,SAAO,WAAW,EAAE;;;;;;CAOtB,aAAa,OAAc,OAAqB,EAAE,EAAmC;EACnF,MAAM,gBAAgB,MAAM,EAAE,EAAE,KAAK,gBAAgB,KAAK;EAC1D,MAAM,EAAE,OAAO,MAAM,oBAAS,SAAS,GAAG,SAAS;AACnD,OAAK,UAAU,KAAK,aAAa,OAAO,cAAc;AACtD,MAAI,MAAM;AACR,QAAK,QAAQ,kBAAkB,KAAK,QAAQ,mBAAmB;AAC/D,QAAK,SAAS,KAAK,UAAU;AAC7B,QAAK,OAAO,KAAK,UAAU,KAAK;;AAElC,MAAIC,UACF,MAAK,SAAS,YAAY,QAAQA,UAAQ;EAE5C,MAAM,CAAC,KAAK,UAAU,KAAK,SAAS,OAAO,cAAc;AAEzD,SAAO;GADK,IAAI,QAAQ,KAAK,KAAK;GACrB;GAAe;GAAO;;;;;;;CAQrC,MAAM,MAAM,OAAc,OAAqB,EAAE,EAAgC;EAC/E,MAAM,CAAC,MAAM,WAAW,KAAK,aAAa,OAAO,KAAK;EACtD,MAAM,eAAe,QAAQ,WAAW,KAAK;EAC7C,IAAI,UAAU;AACd,SAAO,UAAU,aAAa;AAC5B;GACA,MAAM,CAAC,OAAO,KAAK,aAAa,OAAO,KAAK;GAC5C,MAAM,MAAM,MAAM,MAAM,IAAI,CACzB,MAAM,MAAM;AACX,QAAI,CAAC,EAAE,GAAI,OAAM,IAAI,MAAM,EAAE,WAAW;AACxC,WAAO;KACP,CACD,MAAM,OAAO,UAAU;AACtB,QAAI,UAAU,aAAa;KACzB,MAAM,OAAO,UAAU;AACvB,aAAQ,KAAK,GAAG,IAAI,OAAO,GAAG,IAAI,IAAI,YAAY,QAAQ,MAAM,YAAY,IAAI,MAAM;AACtF,WAAM,IAAI,SAAS,YAAY,WAAW,SAAS,KAAK,CAAC;UAEzD,OAAM,IAAI,MAAM,MAAM;KAExB;AACJ,OAAI,IAAK,QAAO,CAAC,KAAK,IAAI;;AAE5B,QAAM,IAAI,MAAM,mBAAmB,KAAK,MAAM;;CAGhD,MAAM,UAAU,OAAc,OAAqB,EAAE,EAAwC;AAC3F,SAAO,KAAK,MAAM,OAAO,KAAK,CAAC,KAAK,OAAO,CAAC,KAAK,SAAS;AAExD,UAAO;IADM,MAAM,IAAI,MAAM;IACf;IAAK;IAAI;IACvB;;CAGJ,MAAM,UAAa,OAAc,OAAqB,EAAE,EAAmC;AACzF,SAAO,KAAK,UAAU,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,KAAK,SAAS;GAAC,KAAK,MAAM,IAAI;GAAO;GAAK;GAAI,CAAC;;;;;;;;;ACzHlG,IAAa,SAAb,MAAoB;;;;;;CAMlB,OAAO,KAAK,YAAoC,OAAO,oBAAmB,IAAI,MAAM,EAAE;AACpF,MAAI,cAAc,MAAO,QAAO,UAAU,EAAE;AAC5C,MAAI,cAAc,MAAO,QAAO,OAAO,GAAG,aAAa;AACvD,SAAO,OAAO,GAAG,UAAU;;;;;CAM7B,OAAO,MAAM,GAAW,SAAS,GAAG;AAClC,SAAO,IAAI,KAAK,aAAa,SAAS,EAAE,uBAAuB,QAAQ,CAAC,CAAC,OAAO,EAAE;;;;;CAMpF,OAAO,GAAG,IAAY;AACpB,MAAI,KAAK,IAAM,QAAO,GAAG,KAAK,MAAM,GAAG,CAAC;EACxC,MAAM,IAAI,KAAK;AACf,MAAI,IAAI,GAAI,QAAO,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC;EACvC,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG;AAC5B,MAAI,IAAI,GAAI,QAAO,GAAG,EAAE,IAAI,KAAK,MAAM,EAAE,GAAG,GAAG;EAC/C,MAAM,IAAI,KAAK,MAAM,IAAI,GAAG;AAC5B,MAAI,IAAI,GAAI,QAAO,GAAG,EAAE,IAAI,IAAI,GAAG;AAEnC,SAAO,GADG,KAAK,MAAM,IAAI,GAAG,CAChB,IAAI,IAAI,GAAG;;CAGzB,OAAO,MAAM,GAAW;EACtB,MAAM,SAAS;GAAC;GAAK;GAAM;GAAM;GAAM;GAAK;EAC5C,IAAI,SAAS;AACb,SAAO,KAAK,QAAQ,OAAO,SAAS,IAAI;AACtC,OAAI,IAAI;AACR;;AAEF,SAAO,GAAG,KAAK,MAAM,GAAG,EAAE,CAAC,GAAG,OAAO;;;;;;AC5BzC,IAAa,MAAb,MAAiB;CAEf,OAAO,SAAS,QAAQ,IAAI,qBAAqB,yBAAyB,QAAQ,IAAI,wBAAwB;;;;CAK9G,QAAOC,SAAU,OAAc;AAC7B,MAAI,MAAM,SAAS,WAAW,EAC5B,SAAQ,IAAI,KAAK,UAAU,SAAS;GAAE,GAAG;GAAO,SAAS,MAAM,QAAQ;GAAI,CAAC,CAAC,CAAC;MAE9E,SAAQ,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;;;;;CAOhD,QAAOC,UAAW,OAAc,OAAsB;AACpD,MAAI,MAAM,QAAS,SAAQ,IAAI,MAAM,IAAI,MAAM,SAAS,IAAI,MAAM,UAAU,CAAC;AAC7E,QAAM,SAAS,SAAS,WAAW;AACjC,WAAQ,IAAI,QAAQ,QAAQ;IAAE,OAAO;IAAI,aAAa;IAAK,SAAS;IAAM,QAAQ;IAAM,CAAC,CAAC;IAC1F;;CAGJ,QAAOC,IAAK,SAAkB,GAAG,OAAkB;EACjD,MAAM,EAAE,SAAS,YAAY,KAAK,QAAQ,GAAG,MAAM;AAGnD,MADiB,QAAQ,IAAI,cAAc,UAAa,QAAQ,IAAI,kBAAkB,QACxE;AACZ,SAAKF,SAAU;IAAE;IAAS,UAAU,QAAQ;IAAU;IAAS,CAAC;AAChE,UAAO;IAAE;IAAS;IAAS;IAAS;;AAGtC,MAAI,CAAC,KAAK,OACR,OAAKC,UAAW;GAAE;GAAS,UAAU,QAAQ;GAAU;GAAS,EAAE,QAAQ,MAAM;AAElF,SAAO;GAAE;GAAS;GAAS;GAAS;;;;;CAMtC,OAAO,QAAQ,GAAG,OAA4D;EAC5E,IAAI,CAAC,OAAO,GAAG,QAAQ;AACvB,MAAI,OAAO,UAAU,SACnB,QAAO;GAAE,SAAS;GAAO,SAAS;GAAM;AAG1C,MAAI,aAAa,MAAM,IAAI,OAAO,MAAM,eAAe,UAAU;GAC/D,MAAM,EAAE,SAAS,GAAG,iBAAiB;AACrC,UAAO;IAAE;IAAS,SAAS,CAAC,cAAc,GAAG,KAAK;IAAE;;AAEtD,SAAO,EAAE,SAAS,OAAO;;;;;CAM3B,OAAO,MAAM,GAAG,OAAkB;EAChC,MAAM,EAAE,YAAY,MAAKC,IAAK;GAAE,UAAU;GAAS,OAAO,MAAM;GAAK,EAAE,GAAG,MAAM;AAChF,QAAM,IAAI,MAAM,QAAQ;;CAG1B,OAAO,KAAK,GAAG,OAAkB;AAC/B,SAAO,MAAKA,IAAK;GAAE,UAAU;GAAW,OAAO,MAAM;GAAQ,EAAE,GAAG,MAAM;;CAG1E,OAAO,OAAO,GAAG,OAAkB;AACjC,SAAO,MAAKA,IAAK;GAAE,UAAU;GAAU,OAAO,MAAM;GAAM,EAAE,GAAG,MAAM;;CAGvE,OAAO,KAAK,GAAG,OAAkB;AAC/B,SAAO,MAAKA,IAAK;GAAE,UAAU;GAAQ,OAAO,MAAM;GAAO,EAAE,GAAG,MAAM;;CAGtE,OAAO,MAAM,GAAG,OAAkB;AAEhC,MADkB,QAAQ,KAAK,MAAM,QAAQ,IAAI,SAAS,UAAU,CAAC,IAAI,QAAQ,IAAI,UAAU,UAC9E,QAAQ,IAAI,aAAa,aACxC,QAAO,MAAKA,IAAK;GAAE,UAAU;GAAS,OAAO,MAAM;GAAM,EAAE,GAAG,MAAM;;;;;;ACjG1E,eAAsB,QAAQ,IAAY;AACxC,QAAO,IAAI,SAAS,YAAY;AAC9B,aAAW,SAAS,GAAG;GACvB;;;;;ACCJ,IAAa,aAAb,MAAwB;CACtB;CACA,QAAQ,GAAG,2BAA2B,aAAa;CACnD;CACA;CAEA,YAAY,YAAoB,WAAsD,EAAE,EAAE;AACxF,OAAK,aAAa;EAClB,MAAM,EAAE,QAAQ,GAAG,eAAe;AAClC,OAAK,SAAS,UAAU;AAUxB,OAAK,aAAa,MATM;GACtB,MAAM;GACN,iBAAiB;IACf,cAAc;IACd,gBAAgB;IACjB;GACD,YAAY;GACZ,gBAAgB;GACjB,EACwC,WAAW;;CAGtD,MAAM,UAAU,MAAc,UAAiB;EAC7C,MAAM,UAAU,SAAS,KAAK,MAAO,OAAO,MAAM,WAAW,IAAI,KAAK,UAAU,EAAE,CAAE;AACpF,QAAM,KAAK,MAAM,UAAU;GAAE;GAAM;GAAS,CAAC;;CAG/C,MAAM,WAAW;EACf,MAAM,YAAY,IAAI,GAAG,WAAW;AACpC,YAAU,SAAS,KAAK,MAAM;AAK9B,UAJe,MAAM,GAAG,UAAU;GAChC;GACA,GAAG,KAAK;GACT,CAAC,EACY,MAAM,KAAK,KAAK;;CAGhC,MAAM,SAAS;EACb,MAAM,SAAS,MAAM,KAAK,UAAU;AACpC,KAAG,UAAU,KAAK,QAAQ,EAAE,WAAW,MAAM,CAAC;AAC9C,KAAG,cAAc,GAAG,KAAK,OAAO,GAAG,KAAK,WAAW,QAAQ,OAAO"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brianbuie/node-kit",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "license": "ISC",
5
5
  "description": "Basic tools for Node.js projects",
6
6
  "author": "Brian Buie <brian@buie.dev>",
@@ -31,12 +31,14 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@types/lodash-es": "^4.17.12",
34
+ "@types/mime-types": "^3.0.1",
34
35
  "@types/node": "^24.9.1",
35
36
  "chalk": "^5.6.2",
36
37
  "date-fns": "^4.1.0",
37
38
  "extract-domain": "^5.0.2",
38
39
  "fast-csv": "^5.0.5",
39
40
  "lodash-es": "^4.17.21",
41
+ "mime-types": "^3.0.2",
40
42
  "quicktype-core": "^23.2.6",
41
43
  "sanitize-filename": "^1.6.3"
42
44
  },
package/src/Dir.test.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
+ import path from 'node:path';
3
4
  import { Dir, TempDir, temp } from './Dir.ts';
4
5
 
5
6
  describe('Dir', () => {
6
7
  const testDir = temp.dir('dir-test');
7
8
 
9
+ console.log(path.join('/dir1', '/dir2', 'dir3'));
10
+
8
11
  it('Sanitizes filenames', () => {
9
12
  const name = testDir.sanitize(':/something/else.json');
10
13
  assert(!name.includes('/'));
@@ -23,13 +26,9 @@ describe('Dir', () => {
23
26
  assert(sub instanceof TempDir);
24
27
  });
25
28
 
26
- it('.dir() and .tempDir() throw on absolute path input', () => {
27
- assert.throws(() => {
28
- testDir.dir(testDir.path);
29
- });
30
- assert.throws(() => {
31
- testDir.tempDir(testDir.path);
32
- });
29
+ it('.dir() and .tempDir() make relative paths', () => {
30
+ assert(testDir.dir('/').path.includes(testDir.path));
31
+ assert(testDir.tempDir('/').path.includes(testDir.path));
33
32
  });
34
33
 
35
34
  it('Resolves filenames in folder', () => {
package/src/Dir.ts CHANGED
@@ -5,7 +5,7 @@ import { File } from './File.ts';
5
5
 
6
6
  /**
7
7
  * Reference to a specific directory with methods to create and list files.
8
- * Default path: './'
8
+ * Default path: '.'
9
9
  * > Created on file system the first time .path is read or any methods are used
10
10
  */
11
11
  export class Dir {
@@ -15,8 +15,8 @@ export class Dir {
15
15
  /**
16
16
  * @param path can be relative to workspace or absolute
17
17
  */
18
- constructor(inputPath = './') {
19
- this.#inputPath = path.resolve(inputPath);
18
+ constructor(inputPath = '.') {
19
+ this.#inputPath = inputPath;
20
20
  }
21
21
 
22
22
  /**
@@ -30,30 +30,25 @@ export class Dir {
30
30
  return this.#resolved;
31
31
  }
32
32
 
33
- notAbsolute(subPath: string) {
34
- if (path.isAbsolute(subPath)) throw new Error(`Absolute path provided: "${subPath}"`);
35
- return subPath;
36
- }
37
-
38
33
  /**
39
34
  * Create a new Dir inside the current Dir
40
- * @param subPath relative path to create (not absolute)
35
+ * @param subPath joined with parent Dir's path to make new Dir
41
36
  * @example
42
37
  * const folder = new Dir('example');
43
- * // folder.path = '/absolute/path/to/example'
38
+ * // folder.path = '/path/to/cwd/example'
44
39
  * const child = folder.dir('path/to/dir');
45
- * // child.path = '/absolute/path/to/example/path/to/dir'
40
+ * // child.path = '/path/to/cwd/example/path/to/dir'
46
41
  */
47
42
  dir(subPath: string) {
48
- return new Dir(path.resolve(this.path, this.notAbsolute(subPath)));
43
+ return new Dir(path.join(this.path, subPath));
49
44
  }
50
45
 
51
46
  /**
52
47
  * Creates a new TempDir inside current Dir
53
- * @param subPath relative path to create (not absolute)
48
+ * @param subPath joined with parent Dir's path to make new TempDir
54
49
  */
55
50
  tempDir(subPath: string) {
56
- return new TempDir(path.resolve(this.path, this.notAbsolute(subPath)));
51
+ return new TempDir(path.join(this.path, subPath));
57
52
  }
58
53
 
59
54
  sanitize(filename: string) {
package/src/File.test.ts CHANGED
@@ -23,12 +23,16 @@ describe('File', () => {
23
23
  });
24
24
  assert(img.exists);
25
25
  });
26
+
27
+ it('Sets correct content type from .ext', () => {
28
+ assert.equal(new File('test1').type, undefined);
29
+ assert.equal(new File('test2.json').type, 'application/json');
30
+ assert.equal(new File('test3.jpg').type, 'image/jpeg');
31
+ });
26
32
  });
27
33
 
28
34
  describe('FileType', () => {
29
35
  it('Creates instances', () => {
30
- const test1 = new File.FileType(testDir.filepath('test1.txt'));
31
- assert(test1.file.path.includes('test1.txt'));
32
36
  const base = 'test2';
33
37
  const eg1 = new File.json(testDir.filepath(base));
34
38
  const eg2 = testDir.file(base).json();
package/src/File.ts CHANGED
@@ -2,38 +2,51 @@ import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
3
  import { Readable } from 'node:stream';
4
4
  import { finished } from 'node:stream/promises';
5
+ import mime from 'mime-types';
5
6
  import { writeToStream, parseStream } from 'fast-csv';
6
7
  import { snapshot } from './snapshot.ts';
7
8
 
8
9
  /**
9
- * > ⚠️ WARNING: API will change!
10
+ * Represents a file on the file system. If the file doesn't exist, it is created the first time it is written to.
10
11
  */
11
12
  export class File {
12
13
  path;
13
14
  root;
14
15
  dir;
15
16
  base;
16
- ext;
17
17
  name;
18
+ ext;
19
+ type;
18
20
 
19
21
  constructor(filepath: string) {
20
- this.path = filepath;
21
- const { root, dir, base, ext, name } = path.parse(filepath);
22
+ this.path = path.resolve(filepath);
23
+ const { root, dir, base, ext, name } = path.parse(this.path);
22
24
  this.root = root;
23
25
  this.dir = dir;
24
26
  this.base = base;
25
- this.ext = ext;
26
27
  this.name = name;
28
+ this.ext = ext;
29
+ this.type = mime.lookup(ext) || undefined;
27
30
  }
28
31
 
29
32
  get exists() {
30
33
  return fs.existsSync(this.path);
31
34
  }
32
35
 
36
+ get stats(): Partial<fs.Stats> {
37
+ return this.exists ? fs.statSync(this.path) : {};
38
+ }
39
+
40
+ /**
41
+ * Deletes the file if it exists
42
+ */
33
43
  delete() {
34
44
  fs.rmSync(this.path, { force: true });
35
45
  }
36
46
 
47
+ /**
48
+ * @returns the contents of the file as a string, or undefined if the file doesn't exist
49
+ */
37
50
  read() {
38
51
  return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;
39
52
  }
@@ -43,7 +56,7 @@ export class File {
43
56
  */
44
57
  lines() {
45
58
  const contents = (this.read() || '').split('\n');
46
- return contents.slice(0, contents.length - 1);
59
+ return contents.at(-1)?.length ? contents : contents.slice(0, contents.length - 1);
47
60
  }
48
61
 
49
62
  get readStream() {
@@ -51,12 +64,12 @@ export class File {
51
64
  }
52
65
 
53
66
  get writeStream() {
54
- fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
67
+ fs.mkdirSync(this.dir, { recursive: true });
55
68
  return fs.createWriteStream(this.path);
56
69
  }
57
70
 
58
71
  write(contents: string | ReadableStream) {
59
- fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
72
+ fs.mkdirSync(this.dir, { recursive: true });
60
73
  if (typeof contents === 'string') return fs.writeFileSync(this.path, contents);
61
74
  if (contents instanceof ReadableStream) return finished(Readable.from(contents).pipe(this.writeStream));
62
75
  throw new Error(`Invalid content type: ${typeof contents}`);
@@ -72,26 +85,51 @@ export class File {
72
85
  fs.appendFileSync(this.path, contents + '\n');
73
86
  }
74
87
 
75
- static get FileType() {
76
- return FileType;
77
- }
78
-
88
+ /**
89
+ * @returns FileTypeJson adaptor for current File, adds '.json' extension if not present.
90
+ * @example
91
+ * const file = new File('./data').json({ key: 'val' }); // FileTypeJson<{ key: string; }>
92
+ * console.log(file.path) // '/path/to/cwd/data.json'
93
+ * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
94
+ * @example
95
+ * const file = new File('./data').json<object>({ key: 'val' }); // FileTypeJson<object>
96
+ * file.write({ something: 'else' }) // ✅ data is typed as object
97
+ */
79
98
  json<T>(contents?: T) {
80
99
  return new FileTypeJson<T>(this.path, contents);
81
100
  }
82
101
 
102
+ /**
103
+ * @example
104
+ * const file = new File.json('data.json', { key: 'val' }); // FileTypeJson<{ key: string; }>
105
+ */
83
106
  static get json() {
84
107
  return FileTypeJson;
85
108
  }
86
109
 
110
+ /**
111
+ * @returns FileTypeNdjson adaptor for current File, adds '.ndjson' extension if not present.
112
+ */
87
113
  ndjson<T extends object>(lines?: T | T[]) {
88
114
  return new FileTypeNdjson<T>(this.path, lines);
89
115
  }
90
-
116
+ /**
117
+ * @example
118
+ * const file = new File.ndjson('log', { key: 'val' }); // FileTypeNdjson<{ key: string; }>
119
+ * console.log(file.path) // /path/to/cwd/log.ndjson
120
+ */
91
121
  static get ndjson() {
92
122
  return FileTypeNdjson;
93
123
  }
94
124
 
125
+ /**
126
+ * @returns FileTypeCsv adaptor for current File, adds '.csv' extension if not present.
127
+ * @example
128
+ * const file = await new File('a').csv([{ col: 'val' }, { col: 'val2' }]); // FileTypeCsv<{ col: string; }>
129
+ * await file.write([ { col2: 'val2' } ]); // ❌ 'col2' doesn't exist on type { col: string; }
130
+ * await file.write({ col: 'val' }); // ✅ Writes one row
131
+ * await file.write([{ col: 'val2' }, { col: 'val3' }]); // ✅ Writes multiple rows
132
+ */
95
133
  async csv<T extends object>(rows?: T[], keys?: (keyof T)[]) {
96
134
  const csvFile = new FileTypeCsv<T>(this.path);
97
135
  if (rows) await csvFile.write(rows, keys);
@@ -129,7 +167,14 @@ export class FileType {
129
167
 
130
168
  /**
131
169
  * A .json file that maintains data type when reading/writing.
132
- * This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
170
+ * > ⚠️ This is mildly unsafe, important/foreign json files should be validated at runtime!
171
+ * @example
172
+ * const file = new FileTypeJson('./data', { key: 'val' }); // FileTypeJson<{ key: string; }>
173
+ * console.log(file.path) // '/path/to/cwd/data.json'
174
+ * file.write({ something: 'else' }) // ❌ property 'something' doesn't exist on type { key: string; }
175
+ * @example
176
+ * const file = new FileTypeJson<object>('./data', { key: 'val' }); // FileTypeJson<object>
177
+ * file.write({ something: 'else' }) // ✅ data is typed as object
133
178
  */
134
179
  export class FileTypeJson<T> extends FileType {
135
180
  constructor(filepath: string, contents?: T) {
@@ -205,7 +250,6 @@ export class FileTypeCsv<Row extends object> extends FileType {
205
250
  return new Promise<Row[]>((resolve, reject) => {
206
251
  const parsed: Row[] = [];
207
252
  parseStream(this.file.readStream, { headers: true })
208
- .on('error', (e) => reject(e))
209
253
  .on('data', (raw: Record<Key<Row>, string>) => {
210
254
  parsed.push(
211
255
  Object.entries(raw).reduce(
@@ -217,6 +261,7 @@ export class FileTypeCsv<Row extends object> extends FileType {
217
261
  ),
218
262
  );
219
263
  })
264
+ .on('error', (e) => reject(e))
220
265
  .on('end', () => resolve(parsed));
221
266
  });
222
267
  }