@brianbuie/node-kit 0.5.1 → 0.5.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/dist/Cache.d.ts CHANGED
@@ -10,8 +10,8 @@ export declare class Cache<T> {
10
10
  }): void;
11
11
  file: import("./File.js").File;
12
12
  get exists(): boolean;
13
- get path(): string;
14
13
  delete(): void;
14
+ get path(): string;
15
15
  };
16
16
  ttl: number;
17
17
  refresh: () => T | Promise<T>;
package/dist/Dir.js CHANGED
@@ -1,5 +1,5 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
3
  import sanitizeFilename from 'sanitize-filename';
4
4
  import { File } from './File.js';
5
5
  /**
package/dist/Fetcher.d.ts CHANGED
@@ -17,17 +17,26 @@ export type FetchOptions = RequestInit & {
17
17
  */
18
18
  export declare class Fetcher {
19
19
  defaultOptions: {
20
- timeout: number;
21
- retries: number;
22
- retryDelay: number;
23
- } & RequestInit & {
20
+ body?: BodyInit | null;
21
+ cache?: RequestCache;
22
+ credentials?: RequestCredentials;
23
+ headers?: (HeadersInit & Record<string, string>) | undefined;
24
+ integrity?: string;
25
+ keepalive?: boolean;
26
+ method?: string;
27
+ mode?: RequestMode;
28
+ priority?: RequestPriority;
29
+ redirect?: RequestRedirect;
30
+ referrer?: string;
31
+ referrerPolicy?: ReferrerPolicy;
32
+ signal?: AbortSignal | null;
33
+ window?: null;
24
34
  base?: string;
25
35
  query?: Query;
26
- headers?: Record<string, string>;
27
36
  data?: any;
28
- timeout?: number;
29
- retries?: number;
30
- retryDelay?: number;
37
+ timeout: number;
38
+ retries: number;
39
+ retryDelay: number;
31
40
  };
32
41
  constructor(opts?: FetchOptions);
33
42
  /**
@@ -35,6 +44,10 @@ export declare class Fetcher {
35
44
  * Also returns domain, to help with cookies
36
45
  */
37
46
  buildUrl(route: Route, opts?: FetchOptions): [URL, string];
47
+ /**
48
+ * Merges options to get headers. Useful when extending the Fetcher class to add custom auth.
49
+ */
50
+ buildHeaders(route: Route, opts?: FetchOptions): HeadersInit & Record<string, string>;
38
51
  /**
39
52
  * Builds request, merging defaultOptions and provided options
40
53
  * Includes Abort signal for timeout
package/dist/Fetcher.js CHANGED
@@ -8,12 +8,12 @@ import extractDomain from 'extract-domain';
8
8
  export class Fetcher {
9
9
  defaultOptions;
10
10
  constructor(opts = {}) {
11
- const defaultOptions = {
11
+ this.defaultOptions = {
12
12
  timeout: 60000,
13
13
  retries: 0,
14
14
  retryDelay: 3000,
15
+ ...opts,
15
16
  };
16
- this.defaultOptions = merge(defaultOptions, opts);
17
17
  }
18
18
  /**
19
19
  * Build URL with URLSearchParams if query is provided
@@ -39,6 +39,13 @@ export class Fetcher {
39
39
  const domain = extractDomain(url.href);
40
40
  return [url, domain];
41
41
  }
42
+ /**
43
+ * Merges options to get headers. Useful when extending the Fetcher class to add custom auth.
44
+ */
45
+ buildHeaders(route, opts = {}) {
46
+ const { headers } = merge({}, this.defaultOptions, opts);
47
+ return headers || {};
48
+ }
42
49
  /**
43
50
  * Builds request, merging defaultOptions and provided options
44
51
  * Includes Abort signal for timeout
@@ -46,8 +53,8 @@ export class Fetcher {
46
53
  buildRequest(route, opts = {}) {
47
54
  const mergedOptions = merge({}, this.defaultOptions, opts);
48
55
  const { query, data, timeout, retries, ...init } = mergedOptions;
56
+ init.headers = this.buildHeaders(route, mergedOptions);
49
57
  if (data) {
50
- init.headers = init.headers || {};
51
58
  init.headers['content-type'] = init.headers['content-type'] || 'application/json';
52
59
  init.method = init.method || 'POST';
53
60
  init.body = JSON.stringify(data);
@@ -70,7 +77,6 @@ export class Fetcher {
70
77
  let attempt = 0;
71
78
  while (attempt < maxAttempts) {
72
79
  attempt++;
73
- // Rebuild request on every attempt to reset AbortSignal.timeout
74
80
  const [req] = this.buildRequest(route, opts);
75
81
  const res = await fetch(req)
76
82
  .then((r) => {
package/dist/File.d.ts CHANGED
@@ -1,7 +1,13 @@
1
+ import * as fs from 'node:fs';
2
+ /**
3
+ * WARNING: API will change!
4
+ */
1
5
  export declare class File {
2
6
  path: string;
3
7
  constructor(filepath: string);
4
8
  get exists(): boolean;
9
+ createWriteStream(options?: Parameters<typeof fs.createWriteStream>[1]): fs.WriteStream;
10
+ delete(): void;
5
11
  read(): string | undefined;
6
12
  write(contents: string): void;
7
13
  /**
@@ -13,28 +19,35 @@ export declare class File {
13
19
  * @returns lines as strings, removes trailing '\n'
14
20
  */
15
21
  lines(): string[];
16
- delete(): void;
17
- static get Adaptor(): typeof FileAdaptor;
22
+ static get Adaptor(): typeof Adaptor;
18
23
  json<T>(contents?: T): JsonFile<T>;
19
24
  static get json(): typeof JsonFile;
20
- ndjson<T>(lines?: T | T[]): NdjsonFile<T>;
25
+ ndjson<T extends object>(lines?: T | T[]): NdjsonFile<T>;
21
26
  static get ndjson(): typeof NdjsonFile;
27
+ csv<T extends object>(rows?: T[], keys?: (keyof T)[]): Promise<CsvFile<T>>;
28
+ static get csv(): typeof CsvFile;
22
29
  }
23
- declare class FileAdaptor<T = string> {
30
+ declare class Adaptor<T = string> {
24
31
  file: File;
25
32
  constructor(filepath: string, contents?: T);
26
33
  get exists(): boolean;
27
- get path(): string;
28
34
  delete(): void;
35
+ get path(): string;
29
36
  }
30
- declare class JsonFile<T> extends FileAdaptor {
37
+ declare class JsonFile<T> extends Adaptor {
31
38
  constructor(filepath: string, contents?: T);
32
39
  read(): T | undefined;
33
40
  write(contents: T): void;
34
41
  }
35
- declare class NdjsonFile<T> extends FileAdaptor {
42
+ declare class NdjsonFile<T extends object> extends Adaptor {
36
43
  constructor(filepath: string, lines?: T | T[]);
37
44
  append(lines: T | T[]): void;
38
45
  lines(): T[];
39
46
  }
47
+ type Key<T extends object> = keyof T;
48
+ declare class CsvFile<Row extends object> extends Adaptor {
49
+ constructor(filepath: string);
50
+ write(rows: Row[], keys?: Key<Row>[]): Promise<void>;
51
+ read(): Promise<Row[]>;
52
+ }
40
53
  export {};
package/dist/File.js CHANGED
@@ -1,6 +1,10 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { writeToString, parseString } from 'fast-csv';
3
4
  import { snapshot } from './snapshot.js';
5
+ /**
6
+ * WARNING: API will change!
7
+ */
4
8
  export class File {
5
9
  path;
6
10
  constructor(filepath) {
@@ -9,6 +13,12 @@ export class File {
9
13
  get exists() {
10
14
  return fs.existsSync(this.path);
11
15
  }
16
+ createWriteStream(options = {}) {
17
+ return fs.createWriteStream(this.path, options);
18
+ }
19
+ delete() {
20
+ fs.rmSync(this.path, { force: true });
21
+ }
12
22
  read() {
13
23
  return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;
14
24
  }
@@ -33,11 +43,8 @@ export class File {
33
43
  const contents = (this.read() || '').split('\n');
34
44
  return contents.slice(0, contents.length - 1);
35
45
  }
36
- delete() {
37
- fs.rmSync(this.path, { force: true });
38
- }
39
46
  static get Adaptor() {
40
- return FileAdaptor;
47
+ return Adaptor;
41
48
  }
42
49
  json(contents) {
43
50
  return new JsonFile(this.path, contents);
@@ -51,8 +58,17 @@ export class File {
51
58
  static get ndjson() {
52
59
  return NdjsonFile;
53
60
  }
61
+ async csv(rows, keys) {
62
+ const csvFile = new CsvFile(this.path);
63
+ if (rows)
64
+ await csvFile.write(rows, keys);
65
+ return csvFile;
66
+ }
67
+ static get csv() {
68
+ return CsvFile;
69
+ }
54
70
  }
55
- class FileAdaptor {
71
+ class Adaptor {
56
72
  file;
57
73
  constructor(filepath, contents) {
58
74
  this.file = new File(filepath);
@@ -66,14 +82,14 @@ class FileAdaptor {
66
82
  get exists() {
67
83
  return this.file.exists;
68
84
  }
69
- get path() {
70
- return this.file.path;
71
- }
72
85
  delete() {
73
86
  this.file.delete();
74
87
  }
88
+ get path() {
89
+ return this.file.path;
90
+ }
75
91
  }
76
- class JsonFile extends FileAdaptor {
92
+ class JsonFile extends Adaptor {
77
93
  constructor(filepath, contents) {
78
94
  super(filepath.endsWith('.json') ? filepath : filepath + '.json');
79
95
  if (contents)
@@ -87,7 +103,7 @@ class JsonFile extends FileAdaptor {
87
103
  this.file.write(JSON.stringify(snapshot(contents), null, 2));
88
104
  }
89
105
  }
90
- class NdjsonFile extends FileAdaptor {
106
+ class NdjsonFile extends Adaptor {
91
107
  constructor(filepath, lines) {
92
108
  super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
93
109
  if (lines)
@@ -100,3 +116,53 @@ class NdjsonFile extends FileAdaptor {
100
116
  return this.file.lines().map((l) => JSON.parse(l));
101
117
  }
102
118
  }
119
+ class CsvFile extends Adaptor {
120
+ constructor(filepath) {
121
+ super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
122
+ }
123
+ async write(rows, keys) {
124
+ const headerSet = new Set();
125
+ if (keys) {
126
+ for (const key of keys)
127
+ headerSet.add(key);
128
+ }
129
+ else {
130
+ for (const row of rows) {
131
+ for (const key in row)
132
+ headerSet.add(key);
133
+ }
134
+ }
135
+ const headers = Array.from(headerSet);
136
+ const outRows = rows.map((row) => headers.map((key) => row[key]));
137
+ const contents = await writeToString([headers, ...outRows]);
138
+ this.file.write(contents);
139
+ }
140
+ async read() {
141
+ return new Promise((resolve, reject) => {
142
+ const parsed = [];
143
+ const content = this.file.read();
144
+ if (!content)
145
+ return resolve(parsed);
146
+ function parseVal(val) {
147
+ if (val.toLowerCase() === 'false')
148
+ return false;
149
+ if (val.toLowerCase() === 'true')
150
+ return true;
151
+ if (val.length === 0)
152
+ return null;
153
+ if (/^[\.0-9]+$/.test(val))
154
+ return Number(val);
155
+ return val;
156
+ }
157
+ parseString(content, { headers: true })
158
+ .on('error', (e) => reject(e))
159
+ .on('end', () => resolve(parsed))
160
+ .on('data', (raw) => {
161
+ parsed.push(Object.entries(raw).reduce((all, [key, val]) => ({
162
+ ...all,
163
+ [key]: parseVal(val),
164
+ }), {}));
165
+ });
166
+ });
167
+ }
168
+ }
package/dist/Log.js CHANGED
@@ -1,11 +1,10 @@
1
- import fs from 'fs';
2
- import { inspect } from 'util';
1
+ import { inspect } from 'node:util';
3
2
  import { isObjectLike } from 'lodash-es';
4
3
  import chalk from 'chalk';
5
4
  import { snapshot } from './snapshot.js';
6
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
7
5
  export class Log {
8
- static isTest = process.env.npm_package_name === packageJson.name && process.env.npm_lifecycle_event === 'test';
6
+ // Only silence logs when THIS package is running its own tests
7
+ static isTest = process.env.npm_package_name === '@brianbuie/node-kit' && process.env.npm_lifecycle_event === 'test';
9
8
  /**
10
9
  * Gcloud parses JSON in stdout
11
10
  */
@@ -1,4 +1,4 @@
1
- import fs from 'fs';
1
+ import * as fs from 'node:fs';
2
2
  import { merge } from 'lodash-es';
3
3
  import * as qt from 'quicktype-core';
4
4
  export class TypeWriter {
package/dist/_index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { Dir, TempDir, temp } from './Dir.js';
2
2
  export { Cache } from './Cache.js';
3
- export { Fetcher, type Route, Query, FetchOptions } from './Fetcher.js';
3
+ export { Fetcher, type Route, type Query, type FetchOptions } from './Fetcher.js';
4
4
  export { File } from './File.js';
5
5
  export { Jwt } from './Jwt.js';
6
6
  export { Log } from './Log.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brianbuie/node-kit",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "license": "ISC",
5
5
  "repository": {
6
6
  "type": "git",
@@ -13,7 +13,10 @@
13
13
  },
14
14
  "type": "module",
15
15
  "exports": {
16
- ".": "./dist/_index.js"
16
+ ".": {
17
+ "types": "./dist/_index.d.ts",
18
+ "default": "./dist/_index.js"
19
+ }
17
20
  },
18
21
  "files": [
19
22
  "src",
@@ -27,6 +30,7 @@
27
30
  "dependencies": {
28
31
  "chalk": "^5.6.2",
29
32
  "extract-domain": "^5.0.2",
33
+ "fast-csv": "^5.0.5",
30
34
  "jsonwebtoken": "^9.0.2",
31
35
  "lodash-es": "^4.17.21",
32
36
  "quicktype-core": "^23.2.6",
package/src/Dir.ts CHANGED
@@ -1,5 +1,5 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
3
  import sanitizeFilename from 'sanitize-filename';
4
4
  import { File } from './File.js';
5
5
  import { snapshot } from './snapshot.js';
package/src/Fetcher.ts CHANGED
@@ -25,12 +25,12 @@ export class Fetcher {
25
25
  defaultOptions;
26
26
 
27
27
  constructor(opts: FetchOptions = {}) {
28
- const defaultOptions = {
28
+ this.defaultOptions = {
29
29
  timeout: 60000,
30
30
  retries: 0,
31
31
  retryDelay: 3000,
32
+ ...opts,
32
33
  };
33
- this.defaultOptions = merge(defaultOptions, opts);
34
34
  }
35
35
 
36
36
  /**
@@ -56,6 +56,14 @@ export class Fetcher {
56
56
  return [url, domain];
57
57
  }
58
58
 
59
+ /**
60
+ * Merges options to get headers. Useful when extending the Fetcher class to add custom auth.
61
+ */
62
+ buildHeaders(route: Route, opts: FetchOptions = {}) {
63
+ const { headers } = merge({}, this.defaultOptions, opts);
64
+ return headers || {};
65
+ }
66
+
59
67
  /**
60
68
  * Builds request, merging defaultOptions and provided options
61
69
  * Includes Abort signal for timeout
@@ -63,8 +71,8 @@ export class Fetcher {
63
71
  buildRequest(route: Route, opts: FetchOptions = {}): [Request, FetchOptions, string] {
64
72
  const mergedOptions = merge({}, this.defaultOptions, opts);
65
73
  const { query, data, timeout, retries, ...init } = mergedOptions;
74
+ init.headers = this.buildHeaders(route, mergedOptions);
66
75
  if (data) {
67
- init.headers = init.headers || {};
68
76
  init.headers['content-type'] = init.headers['content-type'] || 'application/json';
69
77
  init.method = init.method || 'POST';
70
78
  init.body = JSON.stringify(data);
@@ -88,7 +96,6 @@ export class Fetcher {
88
96
  let attempt = 0;
89
97
  while (attempt < maxAttempts) {
90
98
  attempt++;
91
- // Rebuild request on every attempt to reset AbortSignal.timeout
92
99
  const [req] = this.buildRequest(route, opts);
93
100
  const res = await fetch(req)
94
101
  .then((r) => {
package/src/File.test.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { isEqual } from 'lodash-es';
4
3
  import { temp } from './Dir.js';
5
4
  import { File } from './File.js';
6
5
 
@@ -11,14 +10,14 @@ const thing = {
11
10
  a: 'string',
12
11
  b: 2,
13
12
  c: true,
14
- d: null,
13
+ d: false,
14
+ e: null,
15
15
  };
16
16
 
17
17
  describe('FileAdaptor', () => {
18
18
  it('Creates instances', () => {
19
19
  const test1 = new File.Adaptor(testDir.filepath('test1.txt'));
20
20
  assert(test1.file.path.includes('test1.txt'));
21
-
22
21
  const base = 'test2';
23
22
  const eg1 = new File.json(testDir.filepath(base));
24
23
  const eg2 = testDir.file(base).json();
@@ -38,13 +37,14 @@ describe('FileAdaptor', () => {
38
37
 
39
38
  describe('File.ndjson', () => {
40
39
  it('Appends new lines correctly', () => {
41
- const file = testDir.file('empty-lines').ndjson();
40
+ const file = testDir.file('appends-lines').ndjson();
41
+ file.delete();
42
42
  file.append([thing, thing]);
43
43
  assert(file.lines().length === 2);
44
44
  file.append(thing);
45
45
  assert(file.lines().length === 3);
46
46
  file.lines().forEach((line) => {
47
- assert(isEqual(line, thing));
47
+ assert.deepStrictEqual(line, thing);
48
48
  });
49
49
  });
50
50
 
@@ -60,9 +60,9 @@ describe('File.ndjson', () => {
60
60
  describe('File.json', () => {
61
61
  it('Saves data as json', () => {
62
62
  const file = testDir.file('jsonfile-data').json(thing);
63
- assert(isEqual(file.read(), thing));
63
+ assert.deepStrictEqual(file.read(), thing);
64
64
  file.write(thing);
65
- assert(isEqual(file.read(), thing));
65
+ assert.deepStrictEqual(file.read(), thing);
66
66
  });
67
67
 
68
68
  it('Does not create file when reading', () => {
@@ -72,3 +72,14 @@ describe('File.json', () => {
72
72
  assert(!file.exists);
73
73
  });
74
74
  });
75
+
76
+ describe('File.csv', () => {
77
+ it('Saves data as csv', async () => {
78
+ const things = [thing, thing, thing];
79
+ const file = await testDir.file('csv-data').csv(things);
80
+ const parsed = await file.read();
81
+ parsed.forEach((row) => {
82
+ assert.deepEqual(row, thing);
83
+ });
84
+ });
85
+ });
package/src/File.ts CHANGED
@@ -1,7 +1,11 @@
1
- import fs from 'fs';
2
- import path from 'path';
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { writeToString, parseString } from 'fast-csv';
3
4
  import { snapshot } from './snapshot.js';
4
5
 
6
+ /**
7
+ * WARNING: API will change!
8
+ */
5
9
  export class File {
6
10
  path;
7
11
 
@@ -13,6 +17,14 @@ export class File {
13
17
  return fs.existsSync(this.path);
14
18
  }
15
19
 
20
+ createWriteStream(options: Parameters<typeof fs.createWriteStream>[1] = {}) {
21
+ return fs.createWriteStream(this.path, options);
22
+ }
23
+
24
+ delete() {
25
+ fs.rmSync(this.path, { force: true });
26
+ }
27
+
16
28
  read() {
17
29
  return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;
18
30
  }
@@ -40,12 +52,8 @@ export class File {
40
52
  return contents.slice(0, contents.length - 1);
41
53
  }
42
54
 
43
- delete() {
44
- fs.rmSync(this.path, { force: true });
45
- }
46
-
47
55
  static get Adaptor() {
48
- return FileAdaptor;
56
+ return Adaptor;
49
57
  }
50
58
 
51
59
  json<T>(contents?: T) {
@@ -56,16 +64,26 @@ export class File {
56
64
  return JsonFile;
57
65
  }
58
66
 
59
- ndjson<T>(lines?: T | T[]) {
67
+ ndjson<T extends object>(lines?: T | T[]) {
60
68
  return new NdjsonFile<T>(this.path, lines);
61
69
  }
62
70
 
63
71
  static get ndjson() {
64
72
  return NdjsonFile;
65
73
  }
74
+
75
+ async csv<T extends object>(rows?: T[], keys?: (keyof T)[]) {
76
+ const csvFile = new CsvFile<T>(this.path);
77
+ if (rows) await csvFile.write(rows, keys);
78
+ return csvFile;
79
+ }
80
+
81
+ static get csv() {
82
+ return CsvFile;
83
+ }
66
84
  }
67
85
 
68
- class FileAdaptor<T = string> {
86
+ class Adaptor<T = string> {
69
87
  file;
70
88
 
71
89
  constructor(filepath: string, contents?: T) {
@@ -82,16 +100,16 @@ class FileAdaptor<T = string> {
82
100
  return this.file.exists;
83
101
  }
84
102
 
85
- get path() {
86
- return this.file.path;
87
- }
88
-
89
103
  delete() {
90
104
  this.file.delete();
91
105
  }
106
+
107
+ get path() {
108
+ return this.file.path;
109
+ }
92
110
  }
93
111
 
94
- class JsonFile<T> extends FileAdaptor {
112
+ class JsonFile<T> extends Adaptor {
95
113
  constructor(filepath: string, contents?: T) {
96
114
  super(filepath.endsWith('.json') ? filepath : filepath + '.json');
97
115
  if (contents) this.write(contents);
@@ -107,7 +125,7 @@ class JsonFile<T> extends FileAdaptor {
107
125
  }
108
126
  }
109
127
 
110
- class NdjsonFile<T> extends FileAdaptor {
128
+ class NdjsonFile<T extends object> extends Adaptor {
111
129
  constructor(filepath: string, lines?: T | T[]) {
112
130
  super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
113
131
  if (lines) this.append(lines);
@@ -123,3 +141,55 @@ class NdjsonFile<T> extends FileAdaptor {
123
141
  return this.file.lines().map((l) => JSON.parse(l) as T);
124
142
  }
125
143
  }
144
+
145
+ type Key<T extends object> = keyof T;
146
+
147
+ class CsvFile<Row extends object> extends Adaptor {
148
+ constructor(filepath: string) {
149
+ super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
150
+ }
151
+
152
+ async write(rows: Row[], keys?: Key<Row>[]) {
153
+ const headerSet = new Set<Key<Row>>();
154
+ if (keys) {
155
+ for (const key of keys) headerSet.add(key);
156
+ } else {
157
+ for (const row of rows) {
158
+ for (const key in row) headerSet.add(key);
159
+ }
160
+ }
161
+ const headers = Array.from(headerSet);
162
+ const outRows = rows.map((row) => headers.map((key) => row[key]));
163
+ const contents = await writeToString([headers, ...outRows]);
164
+ this.file.write(contents);
165
+ }
166
+
167
+ async read() {
168
+ return new Promise<Row[]>((resolve, reject) => {
169
+ const parsed: Row[] = [];
170
+ const content = this.file.read();
171
+ if (!content) return resolve(parsed);
172
+ function parseVal(val: string) {
173
+ if (val.toLowerCase() === 'false') return false;
174
+ if (val.toLowerCase() === 'true') return true;
175
+ if (val.length === 0) return null;
176
+ if (/^[\.0-9]+$/.test(val)) return Number(val);
177
+ return val;
178
+ }
179
+ parseString(content, { headers: true })
180
+ .on('error', (e) => reject(e))
181
+ .on('end', () => resolve(parsed))
182
+ .on('data', (raw: Record<Key<Row>, string>) => {
183
+ parsed.push(
184
+ Object.entries(raw).reduce(
185
+ (all, [key, val]) => ({
186
+ ...all,
187
+ [key]: parseVal(val as string),
188
+ }),
189
+ {} as Row
190
+ )
191
+ );
192
+ });
193
+ });
194
+ }
195
+ }
package/src/Log.ts CHANGED
@@ -1,11 +1,8 @@
1
- import fs from 'fs';
2
- import { inspect } from 'util';
1
+ import { inspect } from 'node:util';
3
2
  import { isObjectLike } from 'lodash-es';
4
3
  import chalk, { type ChalkInstance } from 'chalk';
5
4
  import { snapshot } from './snapshot.js';
6
5
 
7
- const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8'));
8
-
9
6
  type Severity = 'DEFAULT' | 'DEBUG' | 'INFO' | 'NOTICE' | 'WARNING' | 'ERROR' | 'CRITICAL' | 'ALERT' | 'EMERGENCY';
10
7
 
11
8
  type Options = {
@@ -20,7 +17,8 @@ type Entry = {
20
17
  };
21
18
 
22
19
  export class Log {
23
- static isTest = process.env.npm_package_name === packageJson.name && process.env.npm_lifecycle_event === 'test';
20
+ // Only silence logs when THIS package is running its own tests
21
+ static isTest = process.env.npm_package_name === '@brianbuie/node-kit' && process.env.npm_lifecycle_event === 'test';
24
22
 
25
23
  /**
26
24
  * Gcloud parses JSON in stdout
package/src/TypeWriter.ts CHANGED
@@ -1,4 +1,4 @@
1
- import fs from 'fs';
1
+ import * as fs from 'node:fs';
2
2
  import { merge } from 'lodash-es';
3
3
  import * as qt from 'quicktype-core';
4
4
 
package/src/_index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { Dir, TempDir, temp } from './Dir.js';
2
2
  export { Cache } from './Cache.js';
3
- export { Fetcher, type Route, Query, FetchOptions } from './Fetcher.js';
3
+ export { Fetcher, type Route, type Query, type FetchOptions } from './Fetcher.js';
4
4
  export { File } from './File.js';
5
5
  export { Jwt } from './Jwt.js';
6
6
  export { Log } from './Log.js';