@brianbuie/node-kit 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
- # Node Kit • ![NPM Version](https://img.shields.io/npm/v/%40brianbuie%2Fnode-kit)
1
+ [![NPM Version](https://img.shields.io/npm/v/%40brianbuie%2Fnode-kit)](https://www.npmjs.com/package/@brianbuie/node-kit)
2
+
3
+ # Node Kit
2
4
 
3
5
  Basic tools for quick node.js projects
4
6
 
@@ -11,6 +11,7 @@ export declare class Cache<T> {
11
11
  file: import("./File.js").File;
12
12
  get exists(): boolean;
13
13
  get path(): string;
14
+ delete(): void;
14
15
  };
15
16
  ttl: number;
16
17
  refresh: () => T | Promise<T>;
@@ -13,10 +13,11 @@ export declare class File {
13
13
  * @returns lines as strings, removes trailing '\n'
14
14
  */
15
15
  lines(): string[];
16
+ delete(): void;
16
17
  static get Adaptor(): typeof FileAdaptor;
17
18
  json<T>(contents?: T): JsonFile<T>;
18
19
  static get json(): typeof JsonFile;
19
- ndjson<T>(contents?: T): NdjsonFile<T>;
20
+ ndjson<T>(lines?: T | T[]): NdjsonFile<T>;
20
21
  static get ndjson(): typeof NdjsonFile;
21
22
  }
22
23
  declare class FileAdaptor<T = string> {
@@ -24,6 +25,7 @@ declare class FileAdaptor<T = string> {
24
25
  constructor(filepath: string, contents?: T);
25
26
  get exists(): boolean;
26
27
  get path(): string;
28
+ delete(): void;
27
29
  }
28
30
  declare class JsonFile<T> extends FileAdaptor {
29
31
  constructor(filepath: string, contents?: T);
@@ -31,7 +33,7 @@ declare class JsonFile<T> extends FileAdaptor {
31
33
  write(contents: T): void;
32
34
  }
33
35
  declare class NdjsonFile<T> extends FileAdaptor {
34
- constructor(filepath: string, contents?: T);
36
+ constructor(filepath: string, lines?: T | T[]);
35
37
  append(lines: T | T[]): void;
36
38
  lines(): T[];
37
39
  }
@@ -33,6 +33,9 @@ export class File {
33
33
  const contents = (this.read() || '').split('\n');
34
34
  return contents.slice(0, contents.length - 1);
35
35
  }
36
+ delete() {
37
+ fs.rmSync(this.path, { force: true });
38
+ }
36
39
  static get Adaptor() {
37
40
  return FileAdaptor;
38
41
  }
@@ -42,8 +45,8 @@ export class File {
42
45
  static get json() {
43
46
  return JsonFile;
44
47
  }
45
- ndjson(contents) {
46
- return new NdjsonFile(this.path, contents);
48
+ ndjson(lines) {
49
+ return new NdjsonFile(this.path, lines);
47
50
  }
48
51
  static get ndjson() {
49
52
  return NdjsonFile;
@@ -66,6 +69,9 @@ class FileAdaptor {
66
69
  get path() {
67
70
  return this.file.path;
68
71
  }
72
+ delete() {
73
+ this.file.delete();
74
+ }
69
75
  }
70
76
  class JsonFile extends FileAdaptor {
71
77
  constructor(filepath, contents) {
@@ -82,10 +88,10 @@ class JsonFile extends FileAdaptor {
82
88
  }
83
89
  }
84
90
  class NdjsonFile extends FileAdaptor {
85
- constructor(filepath, contents) {
91
+ constructor(filepath, lines) {
86
92
  super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
87
- if (contents)
88
- this.append(contents);
93
+ if (lines)
94
+ this.append(lines);
89
95
  }
90
96
  append(lines) {
91
97
  this.file.append(Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines)));
@@ -23,7 +23,7 @@ export class Log {
23
23
  static #toConsole(entry, color) {
24
24
  if (entry.message)
25
25
  console.log(color(`[${entry.severity}] ${entry.message}`));
26
- entry.details?.forEach(detail => {
26
+ entry.details?.forEach((detail) => {
27
27
  console.log(inspect(detail, { depth: 10, breakLength: 100, compact: true, colors: true }));
28
28
  });
29
29
  }
@@ -46,7 +46,7 @@ export class Log {
46
46
  * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
47
47
  */
48
48
  static prepare(...input) {
49
- let [first, ...rest] = input.map(snapshot);
49
+ let [first, ...rest] = input.map((i) => snapshot(i));
50
50
  if (typeof first === 'string')
51
51
  return { message: first, details: rest };
52
52
  // @ts-ignore
@@ -73,7 +73,7 @@ export class Log {
73
73
  return this.#log({ severity: 'INFO', color: chalk.white }, ...input);
74
74
  }
75
75
  static debug(...input) {
76
- const debugging = process.argv.some(arg => arg.includes('--debug')) || process.env.DEBUG !== undefined;
76
+ const debugging = process.argv.some((arg) => arg.includes('--debug')) || process.env.DEBUG !== undefined;
77
77
  if (debugging || process.env.NODE_ENV !== 'production') {
78
78
  return this.#log({ severity: 'DEBUG', color: chalk.gray }, ...input);
79
79
  }
@@ -2,4 +2,4 @@
2
2
  * Allows special objects (Error, Headers, Set) to be included in JSON.stringify output
3
3
  * functions are removed
4
4
  */
5
- export declare function snapshot(i: unknown): any;
5
+ export declare function snapshot(i: unknown, max?: number, depth?: number): any;
@@ -3,19 +3,24 @@ import { isObjectLike } from 'lodash-es';
3
3
  * Allows special objects (Error, Headers, Set) to be included in JSON.stringify output
4
4
  * functions are removed
5
5
  */
6
- export function snapshot(i) {
7
- if (Array.isArray(i))
8
- return i.map(snapshot);
6
+ export function snapshot(i, max = 50, depth = 0) {
7
+ if (Array.isArray(i)) {
8
+ if (depth === max)
9
+ return [];
10
+ return i.map((c) => snapshot(c, max, depth + 1));
11
+ }
9
12
  if (typeof i === 'function')
10
13
  return undefined;
11
14
  if (!isObjectLike(i))
12
15
  return i;
16
+ if (depth === max)
17
+ return {};
13
18
  let output = {};
14
19
  // @ts-ignore If it has an 'entries' function, use that for looping (eg. Set, Map, Headers)
15
20
  if (typeof i.entries === 'function') {
16
21
  // @ts-ignore
17
22
  for (let [k, v] of i.entries()) {
18
- output[k] = snapshot(v);
23
+ output[k] = snapshot(v, max, depth + 1);
19
24
  }
20
25
  return output;
21
26
  }
@@ -23,11 +28,11 @@ export function snapshot(i) {
23
28
  // Get Enumerable, inherited properties
24
29
  const obj = i;
25
30
  for (let key in obj) {
26
- output[key] = snapshot(obj[key]);
31
+ output[key] = snapshot(obj[key], max, depth + 1);
27
32
  }
28
33
  // Get Non-enumberable, own properties
29
- Object.getOwnPropertyNames(obj).forEach(key => {
30
- output[key] = snapshot(obj[key]);
34
+ Object.getOwnPropertyNames(obj).forEach((key) => {
35
+ output[key] = snapshot(obj[key], max, depth + 1);
31
36
  });
32
37
  return output;
33
38
  }
package/package.json CHANGED
@@ -1,24 +1,24 @@
1
1
  {
2
2
  "name": "@brianbuie/node-kit",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "license": "ISC",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/brianbuie/node-kit.git"
8
8
  },
9
9
  "scripts": {
10
- "test": "tsc && node --test \".dist/**/*.test.js\" --quiet",
10
+ "test": "tsc && node --test \"dist/**/*.test.js\" --quiet",
11
11
  "preversion": "npm test",
12
12
  "postversion": "git push --follow-tags"
13
13
  },
14
14
  "type": "module",
15
15
  "exports": {
16
- ".": "./.dist/_index.js"
16
+ ".": "./dist/_index.js"
17
17
  },
18
18
  "files": [
19
19
  "src",
20
- ".dist",
21
- "!.dist/**/*.test.*",
20
+ "dist",
21
+ "!dist/**/*.test.*",
22
22
  "README.md"
23
23
  ],
24
24
  "engines": {
package/src/File.test.ts CHANGED
@@ -15,7 +15,7 @@ const thing = {
15
15
  };
16
16
 
17
17
  describe('FileAdaptor', () => {
18
- it('Instances can be created', () => {
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
21
 
@@ -26,6 +26,14 @@ describe('FileAdaptor', () => {
26
26
  const eg3 = eg3File.json();
27
27
  assert(eg1.path === eg2.path && eg2.path === eg3.path);
28
28
  });
29
+
30
+ it('Deletes files', () => {
31
+ const test = testDir.file('delete-test.txt');
32
+ test.write('test');
33
+ assert.equal(test.read(), 'test');
34
+ test.delete();
35
+ assert.equal(test.exists, false);
36
+ });
29
37
  });
30
38
 
31
39
  describe('File.ndjson', () => {
package/src/File.ts CHANGED
@@ -40,6 +40,10 @@ export class File {
40
40
  return contents.slice(0, contents.length - 1);
41
41
  }
42
42
 
43
+ delete() {
44
+ fs.rmSync(this.path, { force: true });
45
+ }
46
+
43
47
  static get Adaptor() {
44
48
  return FileAdaptor;
45
49
  }
@@ -52,8 +56,8 @@ export class File {
52
56
  return JsonFile;
53
57
  }
54
58
 
55
- ndjson<T>(contents?: T) {
56
- return new NdjsonFile<T>(this.path, contents);
59
+ ndjson<T>(lines?: T | T[]) {
60
+ return new NdjsonFile<T>(this.path, lines);
57
61
  }
58
62
 
59
63
  static get ndjson() {
@@ -81,6 +85,10 @@ class FileAdaptor<T = string> {
81
85
  get path() {
82
86
  return this.file.path;
83
87
  }
88
+
89
+ delete() {
90
+ this.file.delete();
91
+ }
84
92
  }
85
93
 
86
94
  class JsonFile<T> extends FileAdaptor {
@@ -100,9 +108,9 @@ class JsonFile<T> extends FileAdaptor {
100
108
  }
101
109
 
102
110
  class NdjsonFile<T> extends FileAdaptor {
103
- constructor(filepath: string, contents?: T) {
111
+ constructor(filepath: string, lines?: T | T[]) {
104
112
  super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
105
- if (contents) this.append(contents);
113
+ if (lines) this.append(lines);
106
114
  }
107
115
 
108
116
  append(lines: T | T[]) {
package/src/Log.ts CHANGED
@@ -38,7 +38,7 @@ export class Log {
38
38
  */
39
39
  static #toConsole(entry: Entry, color: ChalkInstance) {
40
40
  if (entry.message) console.log(color(`[${entry.severity}] ${entry.message}`));
41
- entry.details?.forEach(detail => {
41
+ entry.details?.forEach((detail) => {
42
42
  console.log(inspect(detail, { depth: 10, breakLength: 100, compact: true, colors: true }));
43
43
  });
44
44
  }
@@ -63,7 +63,7 @@ export class Log {
63
63
  * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
64
64
  */
65
65
  static prepare(...input: unknown[]): { message?: string; details: unknown[] } {
66
- let [first, ...rest] = input.map(snapshot);
66
+ let [first, ...rest] = input.map((i) => snapshot(i));
67
67
  if (typeof first === 'string') return { message: first, details: rest };
68
68
  // @ts-ignore
69
69
  if (isObjectLike(first) && typeof first['message'] === 'string') {
@@ -94,7 +94,7 @@ export class Log {
94
94
  }
95
95
 
96
96
  static debug(...input: unknown[]) {
97
- const debugging = process.argv.some(arg => arg.includes('--debug')) || process.env.DEBUG !== undefined;
97
+ const debugging = process.argv.some((arg) => arg.includes('--debug')) || process.env.DEBUG !== undefined;
98
98
  if (debugging || process.env.NODE_ENV !== 'production') {
99
99
  return this.#log({ severity: 'DEBUG', color: chalk.gray }, ...input);
100
100
  }
@@ -1,6 +1,7 @@
1
1
  import { describe, it } from 'node:test';
2
2
  import assert from 'node:assert';
3
3
  import { snapshot } from './snapshot.js';
4
+ import { temp } from './Dir.js';
4
5
 
5
6
  describe('snapshot', () => {
6
7
  it('Captures Error details', () => {
@@ -32,4 +33,22 @@ describe('snapshot', () => {
32
33
  const shot = snapshot(test) as Record<string, any>;
33
34
  assert(shot.func === undefined);
34
35
  });
36
+
37
+ it('Handles recursive references', () => {
38
+ type Thing = { id: number; thing?: Thing; things: Thing[] };
39
+ function createThing(id: number, thing?: Thing) {
40
+ const newThing: Thing = { id, thing, things: [] };
41
+ if (thing) {
42
+ thing.things.push(newThing);
43
+ }
44
+ return newThing;
45
+ }
46
+ const t1 = createThing(1);
47
+ const t2 = createThing(2, t1);
48
+ const result = snapshot(t1, 20) as Thing;
49
+ const f1 = temp.file('recursive').json(result);
50
+ const parsed = f1.read();
51
+ const f2 = temp.file('recursive2').json(parsed);
52
+ assert.deepEqual(f1.read(), f2.read());
53
+ });
35
54
  });
package/src/snapshot.ts CHANGED
@@ -4,17 +4,21 @@ import { isObjectLike } from 'lodash-es';
4
4
  * Allows special objects (Error, Headers, Set) to be included in JSON.stringify output
5
5
  * functions are removed
6
6
  */
7
- export function snapshot(i: unknown): any {
8
- if (Array.isArray(i)) return i.map(snapshot);
7
+ export function snapshot(i: unknown, max = 50, depth = 0): any {
8
+ if (Array.isArray(i)) {
9
+ if (depth === max) return [];
10
+ return i.map((c) => snapshot(c, max, depth + 1));
11
+ }
9
12
  if (typeof i === 'function') return undefined;
10
13
  if (!isObjectLike(i)) return i;
11
14
 
15
+ if (depth === max) return {};
12
16
  let output: Record<string, any> = {};
13
17
  // @ts-ignore If it has an 'entries' function, use that for looping (eg. Set, Map, Headers)
14
18
  if (typeof i.entries === 'function') {
15
19
  // @ts-ignore
16
20
  for (let [k, v] of i.entries()) {
17
- output[k] = snapshot(v);
21
+ output[k] = snapshot(v, max, depth + 1);
18
22
  }
19
23
  return output;
20
24
  }
@@ -24,12 +28,12 @@ export function snapshot(i: unknown): any {
24
28
  // Get Enumerable, inherited properties
25
29
  const obj: Record<string, any> = i!;
26
30
  for (let key in obj) {
27
- output[key] = snapshot(obj[key]);
31
+ output[key] = snapshot(obj[key], max, depth + 1);
28
32
  }
29
33
 
30
34
  // Get Non-enumberable, own properties
31
- Object.getOwnPropertyNames(obj).forEach(key => {
32
- output[key] = snapshot(obj[key]);
35
+ Object.getOwnPropertyNames(obj).forEach((key) => {
36
+ output[key] = snapshot(obj[key], max, depth + 1);
33
37
  });
34
38
 
35
39
  return output;
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes