@brianbuie/node-kit 0.6.0 → 0.8.0

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/Dir.js CHANGED
@@ -4,7 +4,7 @@ import sanitizeFilename from 'sanitize-filename';
4
4
  import { File } from './File.js';
5
5
  /**
6
6
  * Reference to a specific directory with helpful methods for resolving filepaths,
7
- * sanitizing filenames, and saving files
7
+ * sanitizing filenames, and saving files.
8
8
  */
9
9
  export class Dir {
10
10
  path;
@@ -30,7 +30,7 @@ export class Dir {
30
30
  return new Dir(path.resolve(this.path, subPath));
31
31
  }
32
32
  sanitize(name) {
33
- return sanitizeFilename(name.replace('https://', '').replace('www.', ''), { replacement: '_' });
33
+ return sanitizeFilename(name.replace('https://', '').replace('www.', ''), { replacement: '_' }).slice(-200);
34
34
  }
35
35
  /**
36
36
  * @param base - The file name with extension
package/dist/Fetcher.d.ts CHANGED
@@ -12,7 +12,7 @@ export type FetchOptions = RequestInit & {
12
12
  };
13
13
  /**
14
14
  * Fetcher provides a quick way to set up a basic API connection
15
- * with options applied to every request
15
+ * with options applied to every request.
16
16
  * Includes basic methods for requesting and parsing responses
17
17
  */
18
18
  export declare class Fetcher {
@@ -40,7 +40,7 @@ export declare class Fetcher {
40
40
  };
41
41
  constructor(opts?: FetchOptions);
42
42
  /**
43
- * Build URL with URLSearchParams if query is provided
43
+ * Build URL with URLSearchParams if query is provided.
44
44
  * Also returns domain, to help with cookies
45
45
  */
46
46
  buildUrl(route: Route, opts?: FetchOptions): [URL, string];
@@ -49,14 +49,14 @@ export declare class Fetcher {
49
49
  */
50
50
  buildHeaders(route: Route, opts?: FetchOptions): HeadersInit & Record<string, string>;
51
51
  /**
52
- * Builds request, merging defaultOptions and provided options
52
+ * Builds request, merging defaultOptions and provided options.
53
53
  * Includes Abort signal for timeout
54
54
  */
55
55
  buildRequest(route: Route, opts?: FetchOptions): [Request, FetchOptions, string];
56
56
  /**
57
- * Builds and performs the request, merging provided options with defaultOptions
58
- * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body
59
- * Retries on local or network error, with increasing backoff
57
+ * Builds and performs the request, merging provided options with defaultOptions.
58
+ * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body.
59
+ * Retries on local or network error, with increasing backoff.
60
60
  */
61
61
  fetch(route: Route, opts?: FetchOptions): Promise<[Response, Request]>;
62
62
  fetchText(route: Route, opts?: FetchOptions): Promise<[string, Response, Request]>;
package/dist/Fetcher.js CHANGED
@@ -2,7 +2,7 @@ import { merge } from 'lodash-es';
2
2
  import extractDomain from 'extract-domain';
3
3
  /**
4
4
  * Fetcher provides a quick way to set up a basic API connection
5
- * with options applied to every request
5
+ * with options applied to every request.
6
6
  * Includes basic methods for requesting and parsing responses
7
7
  */
8
8
  export class Fetcher {
@@ -16,7 +16,7 @@ export class Fetcher {
16
16
  };
17
17
  }
18
18
  /**
19
- * Build URL with URLSearchParams if query is provided
19
+ * Build URL with URLSearchParams if query is provided.
20
20
  * Also returns domain, to help with cookies
21
21
  */
22
22
  buildUrl(route, opts = {}) {
@@ -47,7 +47,7 @@ export class Fetcher {
47
47
  return headers || {};
48
48
  }
49
49
  /**
50
- * Builds request, merging defaultOptions and provided options
50
+ * Builds request, merging defaultOptions and provided options.
51
51
  * Includes Abort signal for timeout
52
52
  */
53
53
  buildRequest(route, opts = {}) {
@@ -67,9 +67,9 @@ export class Fetcher {
67
67
  return [req, mergedOptions, domain];
68
68
  }
69
69
  /**
70
- * Builds and performs the request, merging provided options with defaultOptions
71
- * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body
72
- * Retries on local or network error, with increasing backoff
70
+ * Builds and performs the request, merging provided options with defaultOptions.
71
+ * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body.
72
+ * Retries on local or network error, with increasing backoff.
73
73
  */
74
74
  async fetch(route, opts = {}) {
75
75
  const [_req, options] = this.buildRequest(route, opts);
package/dist/File.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as fs from 'node:fs';
1
+ import { Readable } from 'node:stream';
2
2
  /**
3
3
  * WARNING: API will change!
4
4
  */
@@ -6,10 +6,10 @@ export declare class File {
6
6
  path: string;
7
7
  constructor(filepath: string);
8
8
  get exists(): boolean;
9
- createWriteStream(options?: Parameters<typeof fs.createWriteStream>[1]): fs.WriteStream;
10
9
  delete(): void;
11
10
  read(): string | undefined;
12
11
  write(contents: string): void;
12
+ streamFrom(...options: Parameters<(typeof Readable)['from']>): Promise<void>;
13
13
  /**
14
14
  * creates file if it doesn't exist, appends string or array of strings as new lines.
15
15
  * File always ends with '\n', so contents don't need to be read before appending
@@ -19,33 +19,33 @@ export declare class File {
19
19
  * @returns lines as strings, removes trailing '\n'
20
20
  */
21
21
  lines(): string[];
22
- static get Adaptor(): typeof Adaptor;
23
- json<T>(contents?: T): JsonFile<T>;
24
- static get json(): typeof JsonFile;
25
- ndjson<T extends object>(lines?: T | T[]): NdjsonFile<T>;
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
+ static get FileType(): typeof FileType;
23
+ json<T>(contents?: T): FileTypeJson<T>;
24
+ static get json(): typeof FileTypeJson;
25
+ ndjson<T extends object>(lines?: T | T[]): FileTypeNdjson<T>;
26
+ static get ndjson(): typeof FileTypeNdjson;
27
+ csv<T extends object>(rows?: T[], keys?: (keyof T)[]): Promise<FileTypeCsv<T>>;
28
+ static get csv(): typeof FileTypeCsv;
29
29
  }
30
- declare class Adaptor<T = string> {
30
+ export declare class FileType<T = string> {
31
31
  file: File;
32
32
  constructor(filepath: string, contents?: T);
33
33
  get exists(): boolean;
34
34
  delete(): void;
35
35
  get path(): string;
36
36
  }
37
- declare class JsonFile<T> extends Adaptor {
37
+ export declare class FileTypeJson<T> extends FileType {
38
38
  constructor(filepath: string, contents?: T);
39
39
  read(): T | undefined;
40
40
  write(contents: T): void;
41
41
  }
42
- declare class NdjsonFile<T extends object> extends Adaptor {
42
+ export declare class FileTypeNdjson<T extends object> extends FileType {
43
43
  constructor(filepath: string, lines?: T | T[]);
44
44
  append(lines: T | T[]): void;
45
45
  lines(): T[];
46
46
  }
47
47
  type Key<T extends object> = keyof T;
48
- declare class CsvFile<Row extends object> extends Adaptor {
48
+ export declare class FileTypeCsv<Row extends object> extends FileType {
49
49
  constructor(filepath: string);
50
50
  write(rows: Row[], keys?: Key<Row>[]): Promise<void>;
51
51
  read(): Promise<Row[]>;
package/dist/File.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
+ import { Readable } from 'node:stream';
4
+ import { finished } from 'node:stream/promises';
3
5
  import { writeToString, parseString } from 'fast-csv';
4
6
  import { snapshot } from './snapshot.js';
5
7
  /**
@@ -13,9 +15,6 @@ export class File {
13
15
  get exists() {
14
16
  return fs.existsSync(this.path);
15
17
  }
16
- createWriteStream(options = {}) {
17
- return fs.createWriteStream(this.path, options);
18
- }
19
18
  delete() {
20
19
  fs.rmSync(this.path, { force: true });
21
20
  }
@@ -26,6 +25,9 @@ export class File {
26
25
  fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
27
26
  fs.writeFileSync(this.path, contents);
28
27
  }
28
+ async streamFrom(...options) {
29
+ return finished(Readable.from(...options).pipe(fs.createWriteStream(this.path)));
30
+ }
29
31
  /**
30
32
  * creates file if it doesn't exist, appends string or array of strings as new lines.
31
33
  * File always ends with '\n', so contents don't need to be read before appending
@@ -43,32 +45,32 @@ export class File {
43
45
  const contents = (this.read() || '').split('\n');
44
46
  return contents.slice(0, contents.length - 1);
45
47
  }
46
- static get Adaptor() {
47
- return Adaptor;
48
+ static get FileType() {
49
+ return FileType;
48
50
  }
49
51
  json(contents) {
50
- return new JsonFile(this.path, contents);
52
+ return new FileTypeJson(this.path, contents);
51
53
  }
52
54
  static get json() {
53
- return JsonFile;
55
+ return FileTypeJson;
54
56
  }
55
57
  ndjson(lines) {
56
- return new NdjsonFile(this.path, lines);
58
+ return new FileTypeNdjson(this.path, lines);
57
59
  }
58
60
  static get ndjson() {
59
- return NdjsonFile;
61
+ return FileTypeNdjson;
60
62
  }
61
63
  async csv(rows, keys) {
62
- const csvFile = new CsvFile(this.path);
64
+ const csvFile = new FileTypeCsv(this.path);
63
65
  if (rows)
64
66
  await csvFile.write(rows, keys);
65
67
  return csvFile;
66
68
  }
67
69
  static get csv() {
68
- return CsvFile;
70
+ return FileTypeCsv;
69
71
  }
70
72
  }
71
- class Adaptor {
73
+ export class FileType {
72
74
  file;
73
75
  constructor(filepath, contents) {
74
76
  this.file = new File(filepath);
@@ -89,7 +91,7 @@ class Adaptor {
89
91
  return this.file.path;
90
92
  }
91
93
  }
92
- class JsonFile extends Adaptor {
94
+ export class FileTypeJson extends FileType {
93
95
  constructor(filepath, contents) {
94
96
  super(filepath.endsWith('.json') ? filepath : filepath + '.json');
95
97
  if (contents)
@@ -103,7 +105,7 @@ class JsonFile extends Adaptor {
103
105
  this.file.write(JSON.stringify(snapshot(contents), null, 2));
104
106
  }
105
107
  }
106
- class NdjsonFile extends Adaptor {
108
+ export class FileTypeNdjson extends FileType {
107
109
  constructor(filepath, lines) {
108
110
  super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
109
111
  if (lines)
@@ -116,7 +118,7 @@ class NdjsonFile extends Adaptor {
116
118
  return this.file.lines().map((l) => JSON.parse(l));
117
119
  }
118
120
  }
119
- class CsvFile extends Adaptor {
121
+ export class FileTypeCsv extends FileType {
120
122
  constructor(filepath) {
121
123
  super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
122
124
  }
@@ -1,8 +1,7 @@
1
1
  export { Dir, TempDir, temp } from './Dir.js';
2
2
  export { Cache } from './Cache.js';
3
3
  export { Fetcher, type Route, type Query, type FetchOptions } from './Fetcher.js';
4
- export { File } from './File.js';
5
- export { Jwt } from './Jwt.js';
4
+ export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.js';
6
5
  export { Log } from './Log.js';
7
6
  export { snapshot } from './snapshot.js';
8
7
  export { timeout } from './timeout.js';
@@ -1,8 +1,7 @@
1
1
  export { Dir, TempDir, temp } from './Dir.js';
2
2
  export { Cache } from './Cache.js';
3
3
  export { Fetcher } from './Fetcher.js';
4
- export { File } from './File.js';
5
- export { Jwt } from './Jwt.js';
4
+ export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.js';
6
5
  export { Log } from './Log.js';
7
6
  export { snapshot } from './snapshot.js';
8
7
  export { timeout } from './timeout.js';
package/package.json CHANGED
@@ -1,21 +1,22 @@
1
1
  {
2
2
  "name": "@brianbuie/node-kit",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
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
+ "docs": "node ./node_modules/ts2md/out/src/ts2md.js --firstHeadingLevel=1",
10
11
  "test": "tsc && node --test \"dist/**/*.test.js\" --quiet",
11
- "preversion": "npm test",
12
+ "preversion": "npm test && npm run docs",
12
13
  "postversion": "git push --follow-tags"
13
14
  },
14
15
  "type": "module",
15
16
  "exports": {
16
17
  ".": {
17
- "types": "./dist/_index.d.ts",
18
- "default": "./dist/_index.js"
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
19
20
  }
20
21
  },
21
22
  "files": [
@@ -29,17 +30,17 @@
29
30
  },
30
31
  "dependencies": {
31
32
  "chalk": "^5.6.2",
33
+ "date-fns": "^4.1.0",
32
34
  "extract-domain": "^5.0.2",
33
35
  "fast-csv": "^5.0.5",
34
- "jsonwebtoken": "^9.0.2",
35
36
  "lodash-es": "^4.17.21",
36
37
  "quicktype-core": "^23.2.6",
37
38
  "sanitize-filename": "^1.6.3"
38
39
  },
39
40
  "devDependencies": {
40
- "@types/jsonwebtoken": "^9.0.10",
41
41
  "@types/lodash-es": "^4.17.12",
42
42
  "@types/node": "^24.9.1",
43
+ "ts2md": "^0.2.8",
43
44
  "typescript": "^5.9.3"
44
45
  }
45
46
  }
package/src/Cache.ts CHANGED
@@ -1,27 +1,30 @@
1
+ import { type Duration, isAfter, add } from 'date-fns';
1
2
  import { temp } from './Dir.js';
2
3
 
3
4
  const cacheDir = temp.dir('cache');
4
5
 
6
+ /**
7
+ * Save data to a local file with an expiration.
8
+ * Fresh/stale data is returned with a flag for if it's fresh or not,
9
+ * so stale data can still be used if needed.
10
+ */
5
11
  export class Cache<T> {
6
12
  file;
7
13
  ttl;
8
- refresh;
9
14
 
10
- constructor(key: string, ttl: number, refresh: () => T | Promise<T>) {
11
- this.file = cacheDir.file(key).json<{ createdAt: number; value: T }>();
12
- this.ttl = ttl;
13
- this.refresh = refresh;
15
+ constructor(key: string, ttl: number | Duration, initialData?: T) {
16
+ this.file = cacheDir.file(key).json<{ savedAt: string; data: T }>();
17
+ this.ttl = typeof ttl === 'number' ? { minutes: ttl } : ttl;
18
+ if (initialData) this.write(initialData);
14
19
  }
15
20
 
16
- async read() {
17
- const { createdAt, value } = this.file.read() || {};
18
- if (value && createdAt && createdAt + this.ttl > Date.now()) return value;
19
- return this.write();
21
+ write(data: T) {
22
+ this.file.write({ savedAt: new Date().toUTCString(), data });
20
23
  }
21
24
 
22
- async write() {
23
- const value = await this.refresh();
24
- this.file.write({ createdAt: Date.now(), value });
25
- return value;
25
+ read(): [T | undefined, boolean] {
26
+ const { savedAt, data } = this.file.read() || {};
27
+ const isFresh = Boolean(savedAt && isAfter(add(savedAt, this.ttl), new Date()));
28
+ return [data, isFresh];
26
29
  }
27
30
  }
package/src/Dir.ts CHANGED
@@ -5,7 +5,7 @@ import { File } from './File.js';
5
5
 
6
6
  /**
7
7
  * Reference to a specific directory with helpful methods for resolving filepaths,
8
- * sanitizing filenames, and saving files
8
+ * sanitizing filenames, and saving files.
9
9
  */
10
10
  export class Dir {
11
11
  path;
@@ -35,7 +35,7 @@ export class Dir {
35
35
  }
36
36
 
37
37
  sanitize(name: string) {
38
- return sanitizeFilename(name.replace('https://', '').replace('www.', ''), { replacement: '_' });
38
+ return sanitizeFilename(name.replace('https://', '').replace('www.', ''), { replacement: '_' }).slice(-200);
39
39
  }
40
40
 
41
41
  /**
package/src/Fetcher.ts CHANGED
@@ -18,7 +18,7 @@ export type FetchOptions = RequestInit & {
18
18
 
19
19
  /**
20
20
  * Fetcher provides a quick way to set up a basic API connection
21
- * with options applied to every request
21
+ * with options applied to every request.
22
22
  * Includes basic methods for requesting and parsing responses
23
23
  */
24
24
  export class Fetcher {
@@ -34,7 +34,7 @@ export class Fetcher {
34
34
  }
35
35
 
36
36
  /**
37
- * Build URL with URLSearchParams if query is provided
37
+ * Build URL with URLSearchParams if query is provided.
38
38
  * Also returns domain, to help with cookies
39
39
  */
40
40
  buildUrl(route: Route, opts: FetchOptions = {}): [URL, string] {
@@ -65,7 +65,7 @@ export class Fetcher {
65
65
  }
66
66
 
67
67
  /**
68
- * Builds request, merging defaultOptions and provided options
68
+ * Builds request, merging defaultOptions and provided options.
69
69
  * Includes Abort signal for timeout
70
70
  */
71
71
  buildRequest(route: Route, opts: FetchOptions = {}): [Request, FetchOptions, string] {
@@ -86,9 +86,9 @@ export class Fetcher {
86
86
  }
87
87
 
88
88
  /**
89
- * Builds and performs the request, merging provided options with defaultOptions
90
- * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body
91
- * Retries on local or network error, with increasing backoff
89
+ * Builds and performs the request, merging provided options with defaultOptions.
90
+ * If `opts.data` is provided, method is updated to POST, content-type json, data is stringified in the body.
91
+ * Retries on local or network error, with increasing backoff.
92
92
  */
93
93
  async fetch(route: Route, opts: FetchOptions = {}): Promise<[Response, Request]> {
94
94
  const [_req, options] = this.buildRequest(route, opts);
package/src/File.test.ts CHANGED
@@ -14,9 +14,9 @@ const thing = {
14
14
  e: null,
15
15
  };
16
16
 
17
- describe('FileAdaptor', () => {
17
+ describe('FileType', () => {
18
18
  it('Creates instances', () => {
19
- const test1 = new File.Adaptor(testDir.filepath('test1.txt'));
19
+ const test1 = new File.FileType(testDir.filepath('test1.txt'));
20
20
  assert(test1.file.path.includes('test1.txt'));
21
21
  const base = 'test2';
22
22
  const eg1 = new File.json(testDir.filepath(base));
@@ -35,6 +35,19 @@ describe('FileAdaptor', () => {
35
35
  });
36
36
  });
37
37
 
38
+ describe('File', () => {
39
+ it('Handles request body as stream input', async () => {
40
+ const res = await fetch('https://testingbot.com/free-online-tools/random-avatar/300');
41
+ const img = testDir.file('image.jpg');
42
+ if (res.body) {
43
+ await img.streamFrom(res.body);
44
+ assert(img.exists);
45
+ } else {
46
+ assert(false, 'No response body');
47
+ }
48
+ });
49
+ });
50
+
38
51
  describe('File.ndjson', () => {
39
52
  it('Appends new lines correctly', () => {
40
53
  const file = testDir.file('appends-lines').ndjson();
package/src/File.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
+ import { Readable } from 'node:stream';
4
+ import { finished } from 'node:stream/promises';
3
5
  import { writeToString, parseString } from 'fast-csv';
4
6
  import { snapshot } from './snapshot.js';
5
7
 
@@ -17,10 +19,6 @@ export class File {
17
19
  return fs.existsSync(this.path);
18
20
  }
19
21
 
20
- createWriteStream(options: Parameters<typeof fs.createWriteStream>[1] = {}) {
21
- return fs.createWriteStream(this.path, options);
22
- }
23
-
24
22
  delete() {
25
23
  fs.rmSync(this.path, { force: true });
26
24
  }
@@ -34,6 +32,10 @@ export class File {
34
32
  fs.writeFileSync(this.path, contents);
35
33
  }
36
34
 
35
+ async streamFrom(...options: Parameters<(typeof Readable)['from']>) {
36
+ return finished(Readable.from(...options).pipe(fs.createWriteStream(this.path)));
37
+ }
38
+
37
39
  /**
38
40
  * creates file if it doesn't exist, appends string or array of strings as new lines.
39
41
  * File always ends with '\n', so contents don't need to be read before appending
@@ -52,38 +54,38 @@ export class File {
52
54
  return contents.slice(0, contents.length - 1);
53
55
  }
54
56
 
55
- static get Adaptor() {
56
- return Adaptor;
57
+ static get FileType() {
58
+ return FileType;
57
59
  }
58
60
 
59
61
  json<T>(contents?: T) {
60
- return new JsonFile<T>(this.path, contents);
62
+ return new FileTypeJson<T>(this.path, contents);
61
63
  }
62
64
 
63
65
  static get json() {
64
- return JsonFile;
66
+ return FileTypeJson;
65
67
  }
66
68
 
67
69
  ndjson<T extends object>(lines?: T | T[]) {
68
- return new NdjsonFile<T>(this.path, lines);
70
+ return new FileTypeNdjson<T>(this.path, lines);
69
71
  }
70
72
 
71
73
  static get ndjson() {
72
- return NdjsonFile;
74
+ return FileTypeNdjson;
73
75
  }
74
76
 
75
77
  async csv<T extends object>(rows?: T[], keys?: (keyof T)[]) {
76
- const csvFile = new CsvFile<T>(this.path);
78
+ const csvFile = new FileTypeCsv<T>(this.path);
77
79
  if (rows) await csvFile.write(rows, keys);
78
80
  return csvFile;
79
81
  }
80
82
 
81
83
  static get csv() {
82
- return CsvFile;
84
+ return FileTypeCsv;
83
85
  }
84
86
  }
85
87
 
86
- class Adaptor<T = string> {
88
+ export class FileType<T = string> {
87
89
  file;
88
90
 
89
91
  constructor(filepath: string, contents?: T) {
@@ -109,7 +111,7 @@ class Adaptor<T = string> {
109
111
  }
110
112
  }
111
113
 
112
- class JsonFile<T> extends Adaptor {
114
+ export class FileTypeJson<T> extends FileType {
113
115
  constructor(filepath: string, contents?: T) {
114
116
  super(filepath.endsWith('.json') ? filepath : filepath + '.json');
115
117
  if (contents) this.write(contents);
@@ -125,7 +127,7 @@ class JsonFile<T> extends Adaptor {
125
127
  }
126
128
  }
127
129
 
128
- class NdjsonFile<T extends object> extends Adaptor {
130
+ export class FileTypeNdjson<T extends object> extends FileType {
129
131
  constructor(filepath: string, lines?: T | T[]) {
130
132
  super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
131
133
  if (lines) this.append(lines);
@@ -133,7 +135,7 @@ class NdjsonFile<T extends object> extends Adaptor {
133
135
 
134
136
  append(lines: T | T[]) {
135
137
  this.file.append(
136
- Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines))
138
+ Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines)),
137
139
  );
138
140
  }
139
141
 
@@ -144,7 +146,7 @@ class NdjsonFile<T extends object> extends Adaptor {
144
146
 
145
147
  type Key<T extends object> = keyof T;
146
148
 
147
- class CsvFile<Row extends object> extends Adaptor {
149
+ export class FileTypeCsv<Row extends object> extends FileType {
148
150
  constructor(filepath: string) {
149
151
  super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
150
152
  }
@@ -186,8 +188,8 @@ class CsvFile<Row extends object> extends Adaptor {
186
188
  ...all,
187
189
  [key]: parseVal(val as string),
188
190
  }),
189
- {} as Row
190
- )
191
+ {} as Row,
192
+ ),
191
193
  );
192
194
  });
193
195
  });
@@ -1,8 +1,7 @@
1
1
  export { Dir, TempDir, temp } from './Dir.js';
2
2
  export { Cache } from './Cache.js';
3
3
  export { Fetcher, type Route, type Query, type FetchOptions } from './Fetcher.js';
4
- export { File } from './File.js';
5
- export { Jwt } from './Jwt.js';
4
+ export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.js';
6
5
  export { Log } from './Log.js';
7
6
  export { snapshot } from './snapshot.js';
8
7
  export { timeout } from './timeout.js';
package/dist/Jwt.d.ts DELETED
@@ -1,15 +0,0 @@
1
- import { type JwtPayload, type SignOptions } from 'jsonwebtoken';
2
- type JwtConfig = {
3
- payload: JwtPayload;
4
- options: SignOptions;
5
- seconds: number;
6
- key: string;
7
- };
8
- export declare class Jwt {
9
- #private;
10
- config: JwtConfig;
11
- constructor(config: JwtConfig);
12
- get now(): number;
13
- get token(): string;
14
- }
15
- export {};
package/dist/Jwt.js DELETED
@@ -1,29 +0,0 @@
1
- import { default as jsonwebtoken } from 'jsonwebtoken';
2
- import { merge } from 'lodash-es';
3
- export class Jwt {
4
- config;
5
- #saved;
6
- constructor(config) {
7
- this.config = config;
8
- this.#createToken();
9
- }
10
- get now() {
11
- return Math.floor(Date.now() / 1000);
12
- }
13
- #createToken() {
14
- const exp = this.now + this.config.seconds;
15
- const payload = merge({
16
- iat: this.now,
17
- exp,
18
- }, this.config.payload);
19
- const token = jsonwebtoken.sign(payload, this.config.key, this.config.options);
20
- this.#saved = { token, exp };
21
- return token;
22
- }
23
- get token() {
24
- if (this.#saved && this.#saved.exp > this.now) {
25
- return this.#saved.token;
26
- }
27
- return this.#createToken();
28
- }
29
- }
package/src/Jwt.test.ts DELETED
@@ -1,22 +0,0 @@
1
- import { describe, it } from 'node:test';
2
- import assert from 'node:assert';
3
- import jsonwebtoken from 'jsonwebtoken';
4
- import { Jwt } from './Jwt.js';
5
-
6
- describe('Jwt', () => {
7
- it('Creates a valid JWT', () => {
8
- const key = 'test';
9
- const jwt = new Jwt({
10
- payload: {
11
- example: 'value',
12
- },
13
- options: {
14
- algorithm: 'HS256',
15
- },
16
- seconds: 60,
17
- key,
18
- });
19
- const result = jsonwebtoken.verify(jwt.token, key);
20
- assert(typeof result !== 'string' && result.example === 'value');
21
- });
22
- });