@brianbuie/node-kit 0.9.0 → 0.9.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/File.js DELETED
@@ -1,185 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import { Readable } from 'node:stream';
4
- import { finished } from 'node:stream/promises';
5
- import { writeToStream, parseStream } from 'fast-csv';
6
- import { snapshot } from './snapshot.js';
7
- /**
8
- * WARNING: API will change!
9
- */
10
- export class File {
11
- path;
12
- constructor(filepath) {
13
- this.path = filepath;
14
- }
15
- get exists() {
16
- return fs.existsSync(this.path);
17
- }
18
- delete() {
19
- fs.rmSync(this.path, { force: true });
20
- }
21
- read() {
22
- return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;
23
- }
24
- /**
25
- * @returns lines as strings, removes trailing '\n'
26
- */
27
- lines() {
28
- const contents = (this.read() || '').split('\n');
29
- return contents.slice(0, contents.length - 1);
30
- }
31
- get readStream() {
32
- return this.exists ? fs.createReadStream(this.path) : Readable.from([]);
33
- }
34
- get writeStream() {
35
- fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
36
- return fs.createWriteStream(this.path);
37
- }
38
- write(contents) {
39
- fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
40
- if (typeof contents === 'string')
41
- return fs.writeFileSync(this.path, contents);
42
- if (contents instanceof ReadableStream)
43
- return finished(Readable.from(contents).pipe(this.writeStream));
44
- throw new Error(`Invalid content type: ${typeof contents}`);
45
- }
46
- /**
47
- * creates file if it doesn't exist, appends string or array of strings as new lines.
48
- * File always ends with '\n', so contents don't need to be read before appending
49
- */
50
- append(lines) {
51
- if (!this.exists)
52
- this.write('');
53
- const contents = Array.isArray(lines) ? lines.join('\n') : lines;
54
- fs.appendFileSync(this.path, contents + '\n');
55
- }
56
- static get FileType() {
57
- return FileType;
58
- }
59
- json(contents) {
60
- return new FileTypeJson(this.path, contents);
61
- }
62
- static get json() {
63
- return FileTypeJson;
64
- }
65
- ndjson(lines) {
66
- return new FileTypeNdjson(this.path, lines);
67
- }
68
- static get ndjson() {
69
- return FileTypeNdjson;
70
- }
71
- async csv(rows, keys) {
72
- const csvFile = new FileTypeCsv(this.path);
73
- if (rows)
74
- await csvFile.write(rows, keys);
75
- return csvFile;
76
- }
77
- static get csv() {
78
- return FileTypeCsv;
79
- }
80
- }
81
- /**
82
- * A generic file adaptor, extended by specific file type implementations
83
- */
84
- export class FileType {
85
- file;
86
- constructor(filepath, contents) {
87
- this.file = new File(filepath);
88
- if (contents)
89
- this.file.write(contents);
90
- }
91
- get exists() {
92
- return this.file.exists;
93
- }
94
- get path() {
95
- return this.file.path;
96
- }
97
- delete() {
98
- this.file.delete();
99
- }
100
- }
101
- /**
102
- * A .json file that maintains data type when reading/writing.
103
- * This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
104
- */
105
- export class FileTypeJson extends FileType {
106
- constructor(filepath, contents) {
107
- super(filepath.endsWith('.json') ? filepath : filepath + '.json');
108
- if (contents)
109
- this.write(contents);
110
- }
111
- read() {
112
- const contents = this.file.read();
113
- return contents ? JSON.parse(contents) : undefined;
114
- }
115
- write(contents) {
116
- this.file.write(JSON.stringify(snapshot(contents), null, 2));
117
- }
118
- }
119
- /**
120
- * New-line delimited json file (.ndjson)
121
- * @see https://jsonltools.com/ndjson-format-specification
122
- */
123
- export class FileTypeNdjson extends FileType {
124
- constructor(filepath, lines) {
125
- super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
126
- if (lines)
127
- this.append(lines);
128
- }
129
- append(lines) {
130
- this.file.append(Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines)));
131
- }
132
- lines() {
133
- return this.file.lines().map((l) => JSON.parse(l));
134
- }
135
- }
136
- /**
137
- * Comma separated values (.csv).
138
- * Input rows as objects, keys are used as column headers
139
- */
140
- export class FileTypeCsv extends FileType {
141
- constructor(filepath) {
142
- super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
143
- }
144
- async write(rows, keys) {
145
- const headerSet = new Set();
146
- if (keys) {
147
- for (const key of keys)
148
- headerSet.add(key);
149
- }
150
- else {
151
- for (const row of rows) {
152
- for (const key in row)
153
- headerSet.add(key);
154
- }
155
- }
156
- const headers = Array.from(headerSet);
157
- const outRows = rows.map((row) => headers.map((key) => row[key]));
158
- return finished(writeToStream(this.file.writeStream, [headers, ...outRows]));
159
- }
160
- #parseVal(val) {
161
- if (val.toLowerCase() === 'false')
162
- return false;
163
- if (val.toLowerCase() === 'true')
164
- return true;
165
- if (val.length === 0)
166
- return null;
167
- if (/^[\.0-9]+$/.test(val))
168
- return Number(val);
169
- return val;
170
- }
171
- async read() {
172
- return new Promise((resolve, reject) => {
173
- const parsed = [];
174
- parseStream(this.file.readStream, { headers: true })
175
- .on('error', (e) => reject(e))
176
- .on('data', (raw) => {
177
- parsed.push(Object.entries(raw).reduce((all, [key, val]) => ({
178
- ...all,
179
- [key]: this.#parseVal(val),
180
- }), {}));
181
- })
182
- .on('end', () => resolve(parsed));
183
- });
184
- }
185
- }
package/dist/Log.d.ts DELETED
@@ -1,43 +0,0 @@
1
- import { type ChalkInstance } from 'chalk';
2
- type Severity = 'DEFAULT' | 'DEBUG' | 'INFO' | 'NOTICE' | 'WARNING' | 'ERROR' | 'CRITICAL' | 'ALERT' | 'EMERGENCY';
3
- type Options = {
4
- severity: Severity;
5
- color: ChalkInstance;
6
- };
7
- export declare class Log {
8
- #private;
9
- static isTest: boolean;
10
- /**
11
- * Handle first argument being a string or an object with a 'message' prop
12
- * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
13
- */
14
- static prepare(...input: unknown[]): {
15
- message?: string;
16
- details: unknown[];
17
- };
18
- /**
19
- * Logs error details before throwing
20
- */
21
- static error(...input: unknown[]): void;
22
- static warn(...input: unknown[]): {
23
- message: string | undefined;
24
- details: unknown[];
25
- options: Options;
26
- };
27
- static notice(...input: unknown[]): {
28
- message: string | undefined;
29
- details: unknown[];
30
- options: Options;
31
- };
32
- static info(...input: unknown[]): {
33
- message: string | undefined;
34
- details: unknown[];
35
- options: Options;
36
- };
37
- static debug(...input: unknown[]): {
38
- message: string | undefined;
39
- details: unknown[];
40
- options: Options;
41
- } | undefined;
42
- }
43
- export {};
package/dist/Log.js DELETED
@@ -1,80 +0,0 @@
1
- import { inspect } from 'node:util';
2
- import { isObjectLike } from 'lodash-es';
3
- import chalk from 'chalk';
4
- import { snapshot } from './snapshot.js';
5
- export class Log {
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';
8
- /**
9
- * Gcloud parses JSON in stdout
10
- */
11
- static #toGcloud(entry) {
12
- if (entry.details?.length === 1) {
13
- console.log(JSON.stringify({ ...entry, details: entry.details[0] }));
14
- }
15
- else {
16
- console.log(JSON.stringify(entry));
17
- }
18
- }
19
- /**
20
- * Includes colors and better inspection for logging during dev
21
- */
22
- static #toConsole(entry, color) {
23
- if (entry.message)
24
- console.log(color(`[${entry.severity}] ${entry.message}`));
25
- entry.details?.forEach((detail) => {
26
- console.log(inspect(detail, { depth: 10, breakLength: 100, compact: true, colors: true }));
27
- });
28
- }
29
- static #log(options, ...input) {
30
- const { message, details } = this.prepare(...input);
31
- // https://cloud.google.com/run/docs/container-contract#env-vars
32
- const isGcloud = process.env.K_SERVICE !== undefined || process.env.CLOUD_RUN_JOB !== undefined;
33
- if (isGcloud) {
34
- this.#toGcloud({ message, severity: options.severity, details });
35
- return { message, details, options };
36
- }
37
- // Hide output while testing this package
38
- if (!this.isTest) {
39
- this.#toConsole({ message, severity: options.severity, details }, options.color);
40
- }
41
- return { message, details, options };
42
- }
43
- /**
44
- * Handle first argument being a string or an object with a 'message' prop
45
- * Also snapshots special objects (eg Error, Response) to keep props in later JSON.stringify output
46
- */
47
- static prepare(...input) {
48
- let [first, ...rest] = input.map((i) => snapshot(i));
49
- if (typeof first === 'string')
50
- return { message: first, details: rest };
51
- // @ts-ignore
52
- if (isObjectLike(first) && typeof first['message'] === 'string') {
53
- const { message, ...firstDetails } = first;
54
- return { message, details: [firstDetails, ...rest] };
55
- }
56
- return { details: input };
57
- }
58
- /**
59
- * Logs error details before throwing
60
- */
61
- static error(...input) {
62
- const { message } = this.#log({ severity: 'ERROR', color: chalk.red }, ...input);
63
- throw new Error(message);
64
- }
65
- static warn(...input) {
66
- return this.#log({ severity: 'WARNING', color: chalk.yellow }, ...input);
67
- }
68
- static notice(...input) {
69
- return this.#log({ severity: 'NOTICE', color: chalk.cyan }, ...input);
70
- }
71
- static info(...input) {
72
- return this.#log({ severity: 'INFO', color: chalk.white }, ...input);
73
- }
74
- static debug(...input) {
75
- const debugging = process.argv.some((arg) => arg.includes('--debug')) || process.env.DEBUG !== undefined;
76
- if (debugging || process.env.NODE_ENV !== 'production') {
77
- return this.#log({ severity: 'DEBUG', color: chalk.gray }, ...input);
78
- }
79
- }
80
- }