@brianbuie/node-kit 0.7.0 → 0.9.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/README.md +27 -31
- package/dist/Cache.d.ts +10 -11
- package/dist/Cache.js +14 -17
- package/dist/Dir.js +1 -1
- package/dist/File.d.ts +27 -9
- package/dist/File.js +57 -40
- package/dist/index.d.ts +0 -1
- package/dist/index.js +0 -1
- package/package.json +2 -3
- package/src/Cache.ts +14 -17
- package/src/Dir.ts +1 -1
- package/src/File.test.ts +35 -18
- package/src/File.ts +62 -45
- package/src/index.ts +0 -1
- package/dist/Jwt.d.ts +0 -15
- package/dist/Jwt.js +0 -29
- package/src/Jwt.test.ts +0 -22
- package/src/Jwt.ts +0 -47
package/README.md
CHANGED
|
@@ -32,7 +32,6 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
|
|
|
32
32
|
| [FileTypeCsv](#class-filetypecsv) |
|
|
33
33
|
| [FileTypeJson](#class-filetypejson) |
|
|
34
34
|
| [FileTypeNdjson](#class-filetypendjson) |
|
|
35
|
-
| [Jwt](#class-jwt) |
|
|
36
35
|
| [Log](#class-log) |
|
|
37
36
|
| [TempDir](#class-tempdir) |
|
|
38
37
|
| [TypeWriter](#class-typewriter) |
|
|
@@ -43,16 +42,20 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
|
|
|
43
42
|
|
|
44
43
|
## Class: Cache
|
|
45
44
|
|
|
46
|
-
Save
|
|
45
|
+
Save data to a local file with an expiration.
|
|
46
|
+
Fresh/stale data is returned with a flag for if it's fresh or not,
|
|
47
|
+
so stale data can still be used if needed.
|
|
47
48
|
|
|
48
49
|
```ts
|
|
49
50
|
export class Cache<T> {
|
|
50
51
|
file;
|
|
51
52
|
ttl;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
constructor(key: string, ttl: number | Duration, initialData?: T)
|
|
54
|
+
write(data: T)
|
|
55
|
+
read(): [
|
|
56
|
+
T | undefined,
|
|
57
|
+
boolean
|
|
58
|
+
]
|
|
56
59
|
}
|
|
57
60
|
```
|
|
58
61
|
|
|
@@ -244,12 +247,13 @@ export class File {
|
|
|
244
247
|
path;
|
|
245
248
|
constructor(filepath: string)
|
|
246
249
|
get exists()
|
|
247
|
-
createWriteStream(options: Parameters<typeof fs.createWriteStream>[1] = {})
|
|
248
250
|
delete()
|
|
249
251
|
read()
|
|
250
|
-
write(contents: string)
|
|
251
|
-
append(lines: string | string[])
|
|
252
252
|
lines()
|
|
253
|
+
get readStream()
|
|
254
|
+
get writeStream()
|
|
255
|
+
write(contents: string | ReadableStream)
|
|
256
|
+
append(lines: string | string[])
|
|
253
257
|
static get FileType()
|
|
254
258
|
json<T>(contents?: T)
|
|
255
259
|
static get json()
|
|
@@ -292,13 +296,15 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
|
|
|
292
296
|
---
|
|
293
297
|
## Class: FileType
|
|
294
298
|
|
|
299
|
+
A generic file adaptor, extended by specific file type implementations
|
|
300
|
+
|
|
295
301
|
```ts
|
|
296
|
-
export class FileType
|
|
302
|
+
export class FileType {
|
|
297
303
|
file;
|
|
298
|
-
constructor(filepath: string, contents?:
|
|
304
|
+
constructor(filepath: string, contents?: string)
|
|
299
305
|
get exists()
|
|
300
|
-
delete()
|
|
301
306
|
get path()
|
|
307
|
+
delete()
|
|
302
308
|
}
|
|
303
309
|
```
|
|
304
310
|
|
|
@@ -307,10 +313,14 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
|
|
|
307
313
|
---
|
|
308
314
|
## Class: FileTypeCsv
|
|
309
315
|
|
|
316
|
+
Comma separated values (.csv).
|
|
317
|
+
Input rows as objects, keys are used as column headers
|
|
318
|
+
|
|
310
319
|
```ts
|
|
311
320
|
export class FileTypeCsv<Row extends object> extends FileType {
|
|
312
321
|
constructor(filepath: string)
|
|
313
322
|
async write(rows: Row[], keys?: Key<Row>[])
|
|
323
|
+
#parseVal(val: string)
|
|
314
324
|
async read()
|
|
315
325
|
}
|
|
316
326
|
```
|
|
@@ -322,6 +332,9 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
|
|
|
322
332
|
---
|
|
323
333
|
## Class: FileTypeJson
|
|
324
334
|
|
|
335
|
+
A .json file that maintains data type when reading/writing.
|
|
336
|
+
This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
|
|
337
|
+
|
|
325
338
|
```ts
|
|
326
339
|
export class FileTypeJson<T> extends FileType {
|
|
327
340
|
constructor(filepath: string, contents?: T)
|
|
@@ -337,6 +350,8 @@ Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types
|
|
|
337
350
|
---
|
|
338
351
|
## Class: FileTypeNdjson
|
|
339
352
|
|
|
353
|
+
New-line delimited json file (.ndjson)
|
|
354
|
+
|
|
340
355
|
```ts
|
|
341
356
|
export class FileTypeNdjson<T extends object> extends FileType {
|
|
342
357
|
constructor(filepath: string, lines?: T | T[])
|
|
@@ -349,25 +364,6 @@ See also: [FileType](#class-filetype)
|
|
|
349
364
|
|
|
350
365
|
Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
351
366
|
|
|
352
|
-
---
|
|
353
|
-
## Class: Jwt
|
|
354
|
-
|
|
355
|
-
```ts
|
|
356
|
-
export class Jwt {
|
|
357
|
-
config;
|
|
358
|
-
#saved?: {
|
|
359
|
-
exp: number;
|
|
360
|
-
token: string;
|
|
361
|
-
};
|
|
362
|
-
constructor(config: JwtConfig)
|
|
363
|
-
get now()
|
|
364
|
-
#createToken()
|
|
365
|
-
get token()
|
|
366
|
-
}
|
|
367
|
-
```
|
|
368
|
-
|
|
369
|
-
Links: [API](#api), [Classes](#classes), [Functions](#functions), [Types](#types), [Variables](#variables)
|
|
370
|
-
|
|
371
367
|
---
|
|
372
368
|
## Class: Log
|
|
373
369
|
|
package/dist/Cache.d.ts
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
|
+
import { type Duration } from 'date-fns';
|
|
1
2
|
/**
|
|
2
|
-
* Save
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* @param getValue the function to populate the cache (eg. fetch results, generate key, etc)
|
|
3
|
+
* Save data to a local file with an expiration.
|
|
4
|
+
* Fresh/stale data is returned with a flag for if it's fresh or not,
|
|
5
|
+
* so stale data can still be used if needed.
|
|
6
6
|
*/
|
|
7
7
|
export declare class Cache<T> {
|
|
8
8
|
file: import("./File.js").FileTypeJson<{
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
savedAt: string;
|
|
10
|
+
data: T;
|
|
11
11
|
}>;
|
|
12
|
-
ttl:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
read():
|
|
16
|
-
write(): Promise<T>;
|
|
12
|
+
ttl: Duration;
|
|
13
|
+
constructor(key: string, ttl: number | Duration, initialData?: T);
|
|
14
|
+
write(data: T): void;
|
|
15
|
+
read(): [T | undefined, boolean];
|
|
17
16
|
}
|
package/dist/Cache.js
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
|
+
import { isAfter, add } from 'date-fns';
|
|
1
2
|
import { temp } from './Dir.js';
|
|
2
3
|
const cacheDir = temp.dir('cache');
|
|
3
4
|
/**
|
|
4
|
-
* Save
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* @param getValue the function to populate the cache (eg. fetch results, generate key, etc)
|
|
5
|
+
* Save data to a local file with an expiration.
|
|
6
|
+
* Fresh/stale data is returned with a flag for if it's fresh or not,
|
|
7
|
+
* so stale data can still be used if needed.
|
|
8
8
|
*/
|
|
9
9
|
export class Cache {
|
|
10
10
|
file;
|
|
11
11
|
ttl;
|
|
12
|
-
|
|
13
|
-
constructor(key, ttl, getValue) {
|
|
12
|
+
constructor(key, ttl, initialData) {
|
|
14
13
|
this.file = cacheDir.file(key).json();
|
|
15
|
-
this.ttl = ttl;
|
|
16
|
-
|
|
14
|
+
this.ttl = typeof ttl === 'number' ? { minutes: ttl } : ttl;
|
|
15
|
+
if (initialData)
|
|
16
|
+
this.write(initialData);
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (value && createdAt && createdAt + this.ttl > Date.now())
|
|
21
|
-
return value;
|
|
22
|
-
return this.write();
|
|
18
|
+
write(data) {
|
|
19
|
+
this.file.write({ savedAt: new Date().toUTCString(), data });
|
|
23
20
|
}
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
return
|
|
21
|
+
read() {
|
|
22
|
+
const { savedAt, data } = this.file.read() || {};
|
|
23
|
+
const isFresh = Boolean(savedAt && isAfter(add(savedAt, this.ttl), new Date()));
|
|
24
|
+
return [data, isFresh];
|
|
28
25
|
}
|
|
29
26
|
}
|
package/dist/Dir.js
CHANGED
|
@@ -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/File.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
2
3
|
/**
|
|
3
4
|
* WARNING: API will change!
|
|
4
5
|
*/
|
|
@@ -6,19 +7,20 @@ export declare class File {
|
|
|
6
7
|
path: string;
|
|
7
8
|
constructor(filepath: string);
|
|
8
9
|
get exists(): boolean;
|
|
9
|
-
createWriteStream(options?: Parameters<typeof fs.createWriteStream>[1]): fs.WriteStream;
|
|
10
10
|
delete(): void;
|
|
11
11
|
read(): string | undefined;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @returns lines as strings, removes trailing '\n'
|
|
14
|
+
*/
|
|
15
|
+
lines(): string[];
|
|
16
|
+
get readStream(): fs.ReadStream | Readable;
|
|
17
|
+
get writeStream(): fs.WriteStream;
|
|
18
|
+
write(contents: string | ReadableStream): void | Promise<void>;
|
|
13
19
|
/**
|
|
14
20
|
* creates file if it doesn't exist, appends string or array of strings as new lines.
|
|
15
21
|
* File always ends with '\n', so contents don't need to be read before appending
|
|
16
22
|
*/
|
|
17
23
|
append(lines: string | string[]): void;
|
|
18
|
-
/**
|
|
19
|
-
* @returns lines as strings, removes trailing '\n'
|
|
20
|
-
*/
|
|
21
|
-
lines(): string[];
|
|
22
24
|
static get FileType(): typeof FileType;
|
|
23
25
|
json<T>(contents?: T): FileTypeJson<T>;
|
|
24
26
|
static get json(): typeof FileTypeJson;
|
|
@@ -27,25 +29,41 @@ export declare class File {
|
|
|
27
29
|
csv<T extends object>(rows?: T[], keys?: (keyof T)[]): Promise<FileTypeCsv<T>>;
|
|
28
30
|
static get csv(): typeof FileTypeCsv;
|
|
29
31
|
}
|
|
30
|
-
|
|
32
|
+
/**
|
|
33
|
+
* A generic file adaptor, extended by specific file type implementations
|
|
34
|
+
*/
|
|
35
|
+
export declare class FileType {
|
|
31
36
|
file: File;
|
|
32
|
-
constructor(filepath: string, contents?:
|
|
37
|
+
constructor(filepath: string, contents?: string);
|
|
33
38
|
get exists(): boolean;
|
|
34
|
-
delete(): void;
|
|
35
39
|
get path(): string;
|
|
40
|
+
delete(): void;
|
|
36
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* A .json file that maintains data type when reading/writing.
|
|
44
|
+
* This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
|
|
45
|
+
*/
|
|
37
46
|
export declare class FileTypeJson<T> extends FileType {
|
|
38
47
|
constructor(filepath: string, contents?: T);
|
|
39
48
|
read(): T | undefined;
|
|
40
49
|
write(contents: T): void;
|
|
41
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* New-line delimited json file (.ndjson)
|
|
53
|
+
* @see https://jsonltools.com/ndjson-format-specification
|
|
54
|
+
*/
|
|
42
55
|
export declare class FileTypeNdjson<T extends object> extends FileType {
|
|
43
56
|
constructor(filepath: string, lines?: T | T[]);
|
|
44
57
|
append(lines: T | T[]): void;
|
|
45
58
|
lines(): T[];
|
|
46
59
|
}
|
|
47
60
|
type Key<T extends object> = keyof T;
|
|
61
|
+
/**
|
|
62
|
+
* Comma separated values (.csv).
|
|
63
|
+
* Input rows as objects, keys are used as column headers
|
|
64
|
+
*/
|
|
48
65
|
export declare class FileTypeCsv<Row extends object> extends FileType {
|
|
66
|
+
#private;
|
|
49
67
|
constructor(filepath: string);
|
|
50
68
|
write(rows: Row[], keys?: Key<Row>[]): Promise<void>;
|
|
51
69
|
read(): Promise<Row[]>;
|
package/dist/File.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
import { finished } from 'node:stream/promises';
|
|
5
|
+
import { writeToStream, parseStream } from 'fast-csv';
|
|
4
6
|
import { snapshot } from './snapshot.js';
|
|
5
7
|
/**
|
|
6
8
|
* WARNING: API will change!
|
|
@@ -13,18 +15,33 @@ 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
|
}
|
|
22
21
|
read() {
|
|
23
22
|
return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;
|
|
24
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
|
+
}
|
|
25
38
|
write(contents) {
|
|
26
39
|
fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
|
|
27
|
-
|
|
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}`);
|
|
28
45
|
}
|
|
29
46
|
/**
|
|
30
47
|
* creates file if it doesn't exist, appends string or array of strings as new lines.
|
|
@@ -36,13 +53,6 @@ export class File {
|
|
|
36
53
|
const contents = Array.isArray(lines) ? lines.join('\n') : lines;
|
|
37
54
|
fs.appendFileSync(this.path, contents + '\n');
|
|
38
55
|
}
|
|
39
|
-
/**
|
|
40
|
-
* @returns lines as strings, removes trailing '\n'
|
|
41
|
-
*/
|
|
42
|
-
lines() {
|
|
43
|
-
const contents = (this.read() || '').split('\n');
|
|
44
|
-
return contents.slice(0, contents.length - 1);
|
|
45
|
-
}
|
|
46
56
|
static get FileType() {
|
|
47
57
|
return FileType;
|
|
48
58
|
}
|
|
@@ -68,27 +78,30 @@ export class File {
|
|
|
68
78
|
return FileTypeCsv;
|
|
69
79
|
}
|
|
70
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* A generic file adaptor, extended by specific file type implementations
|
|
83
|
+
*/
|
|
71
84
|
export class FileType {
|
|
72
85
|
file;
|
|
73
86
|
constructor(filepath, contents) {
|
|
74
87
|
this.file = new File(filepath);
|
|
75
|
-
if (contents)
|
|
76
|
-
if (typeof contents !== 'string') {
|
|
77
|
-
throw new Error('File contents must be a string');
|
|
78
|
-
}
|
|
88
|
+
if (contents)
|
|
79
89
|
this.file.write(contents);
|
|
80
|
-
}
|
|
81
90
|
}
|
|
82
91
|
get exists() {
|
|
83
92
|
return this.file.exists;
|
|
84
93
|
}
|
|
85
|
-
delete() {
|
|
86
|
-
this.file.delete();
|
|
87
|
-
}
|
|
88
94
|
get path() {
|
|
89
95
|
return this.file.path;
|
|
90
96
|
}
|
|
97
|
+
delete() {
|
|
98
|
+
this.file.delete();
|
|
99
|
+
}
|
|
91
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
|
+
*/
|
|
92
105
|
export class FileTypeJson extends FileType {
|
|
93
106
|
constructor(filepath, contents) {
|
|
94
107
|
super(filepath.endsWith('.json') ? filepath : filepath + '.json');
|
|
@@ -103,6 +116,10 @@ export class FileTypeJson extends FileType {
|
|
|
103
116
|
this.file.write(JSON.stringify(snapshot(contents), null, 2));
|
|
104
117
|
}
|
|
105
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* New-line delimited json file (.ndjson)
|
|
121
|
+
* @see https://jsonltools.com/ndjson-format-specification
|
|
122
|
+
*/
|
|
106
123
|
export class FileTypeNdjson extends FileType {
|
|
107
124
|
constructor(filepath, lines) {
|
|
108
125
|
super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
|
|
@@ -116,6 +133,10 @@ export class FileTypeNdjson extends FileType {
|
|
|
116
133
|
return this.file.lines().map((l) => JSON.parse(l));
|
|
117
134
|
}
|
|
118
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Comma separated values (.csv).
|
|
138
|
+
* Input rows as objects, keys are used as column headers
|
|
139
|
+
*/
|
|
119
140
|
export class FileTypeCsv extends FileType {
|
|
120
141
|
constructor(filepath) {
|
|
121
142
|
super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
|
|
@@ -134,35 +155,31 @@ export class FileTypeCsv extends FileType {
|
|
|
134
155
|
}
|
|
135
156
|
const headers = Array.from(headerSet);
|
|
136
157
|
const outRows = rows.map((row) => headers.map((key) => row[key]));
|
|
137
|
-
|
|
138
|
-
|
|
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;
|
|
139
170
|
}
|
|
140
171
|
async read() {
|
|
141
172
|
return new Promise((resolve, reject) => {
|
|
142
173
|
const parsed = [];
|
|
143
|
-
|
|
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 })
|
|
174
|
+
parseStream(this.file.readStream, { headers: true })
|
|
158
175
|
.on('error', (e) => reject(e))
|
|
159
|
-
.on('end', () => resolve(parsed))
|
|
160
176
|
.on('data', (raw) => {
|
|
161
177
|
parsed.push(Object.entries(raw).reduce((all, [key, val]) => ({
|
|
162
178
|
...all,
|
|
163
|
-
[key]: parseVal(val),
|
|
179
|
+
[key]: this.#parseVal(val),
|
|
164
180
|
}), {}));
|
|
165
|
-
})
|
|
181
|
+
})
|
|
182
|
+
.on('end', () => resolve(parsed));
|
|
166
183
|
});
|
|
167
184
|
}
|
|
168
185
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ 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
4
|
export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.js';
|
|
5
|
-
export { Jwt } from './Jwt.js';
|
|
6
5
|
export { Log } from './Log.js';
|
|
7
6
|
export { snapshot } from './snapshot.js';
|
|
8
7
|
export { timeout } from './timeout.js';
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,6 @@ export { Dir, TempDir, temp } from './Dir.js';
|
|
|
2
2
|
export { Cache } from './Cache.js';
|
|
3
3
|
export { Fetcher } from './Fetcher.js';
|
|
4
4
|
export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.js';
|
|
5
|
-
export { Jwt } from './Jwt.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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brianbuie/node-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,15 +30,14 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"chalk": "^5.6.2",
|
|
33
|
+
"date-fns": "^4.1.0",
|
|
33
34
|
"extract-domain": "^5.0.2",
|
|
34
35
|
"fast-csv": "^5.0.5",
|
|
35
|
-
"jsonwebtoken": "^9.0.2",
|
|
36
36
|
"lodash-es": "^4.17.21",
|
|
37
37
|
"quicktype-core": "^23.2.6",
|
|
38
38
|
"sanitize-filename": "^1.6.3"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@types/jsonwebtoken": "^9.0.10",
|
|
42
41
|
"@types/lodash-es": "^4.17.12",
|
|
43
42
|
"@types/node": "^24.9.1",
|
|
44
43
|
"ts2md": "^0.2.8",
|
package/src/Cache.ts
CHANGED
|
@@ -1,33 +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
|
|
|
5
6
|
/**
|
|
6
|
-
* Save
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* @param getValue the function to populate the cache (eg. fetch results, generate key, etc)
|
|
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
10
|
*/
|
|
11
11
|
export class Cache<T> {
|
|
12
12
|
file;
|
|
13
13
|
ttl;
|
|
14
|
-
getValue;
|
|
15
14
|
|
|
16
|
-
constructor(key: string, ttl: number
|
|
17
|
-
this.file = cacheDir.file(key).json<{
|
|
18
|
-
this.ttl = ttl;
|
|
19
|
-
this.
|
|
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);
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
if (value && createdAt && createdAt + this.ttl > Date.now()) return value;
|
|
25
|
-
return this.write();
|
|
21
|
+
write(data: T) {
|
|
22
|
+
this.file.write({ savedAt: new Date().toUTCString(), data });
|
|
26
23
|
}
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
return
|
|
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];
|
|
32
29
|
}
|
|
33
30
|
}
|
package/src/Dir.ts
CHANGED
|
@@ -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/File.test.ts
CHANGED
|
@@ -14,6 +14,17 @@ const thing = {
|
|
|
14
14
|
e: null,
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
describe('File', () => {
|
|
18
|
+
it('Handles request body as stream input', async () => {
|
|
19
|
+
const img = testDir.file('image.jpg');
|
|
20
|
+
await fetch('https://testingbot.com/free-online-tools/random-avatar/300').then((res) => {
|
|
21
|
+
if (!res.body) throw new Error('No response body');
|
|
22
|
+
return img.write(res.body);
|
|
23
|
+
});
|
|
24
|
+
assert(img.exists);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
17
28
|
describe('FileType', () => {
|
|
18
29
|
it('Creates instances', () => {
|
|
19
30
|
const test1 = new File.FileType(testDir.filepath('test1.txt'));
|
|
@@ -35,7 +46,23 @@ describe('FileType', () => {
|
|
|
35
46
|
});
|
|
36
47
|
});
|
|
37
48
|
|
|
38
|
-
describe('
|
|
49
|
+
describe('FileTypeJson', () => {
|
|
50
|
+
it('Saves data as json', () => {
|
|
51
|
+
const file = testDir.file('jsonfile-data').json(thing);
|
|
52
|
+
assert.deepStrictEqual(file.read(), thing);
|
|
53
|
+
file.write(thing);
|
|
54
|
+
assert.deepStrictEqual(file.read(), thing);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('Does not create file when reading', () => {
|
|
58
|
+
const file = testDir.file('test123').json();
|
|
59
|
+
const contents = file.read();
|
|
60
|
+
assert(contents === undefined);
|
|
61
|
+
assert(!file.exists);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('FileTypeNdjson', () => {
|
|
39
66
|
it('Appends new lines correctly', () => {
|
|
40
67
|
const file = testDir.file('appends-lines').ndjson();
|
|
41
68
|
file.delete();
|
|
@@ -57,23 +84,7 @@ describe('File.ndjson', () => {
|
|
|
57
84
|
});
|
|
58
85
|
});
|
|
59
86
|
|
|
60
|
-
describe('
|
|
61
|
-
it('Saves data as json', () => {
|
|
62
|
-
const file = testDir.file('jsonfile-data').json(thing);
|
|
63
|
-
assert.deepStrictEqual(file.read(), thing);
|
|
64
|
-
file.write(thing);
|
|
65
|
-
assert.deepStrictEqual(file.read(), thing);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('Does not create file when reading', () => {
|
|
69
|
-
const file = testDir.file('test123').json();
|
|
70
|
-
const contents = file.read();
|
|
71
|
-
assert(contents === undefined);
|
|
72
|
-
assert(!file.exists);
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe('File.csv', () => {
|
|
87
|
+
describe('FileTypeCsv', () => {
|
|
77
88
|
it('Saves data as csv', async () => {
|
|
78
89
|
const things = [thing, thing, thing];
|
|
79
90
|
const file = await testDir.file('csv-data').csv(things);
|
|
@@ -82,4 +93,10 @@ describe('File.csv', () => {
|
|
|
82
93
|
assert.deepEqual(row, thing);
|
|
83
94
|
});
|
|
84
95
|
});
|
|
96
|
+
it('Reads file that does not exist', async () => {
|
|
97
|
+
const file = await testDir.file('bogus').csv();
|
|
98
|
+
const contents = await file.read();
|
|
99
|
+
assert(Array.isArray(contents));
|
|
100
|
+
assert(contents.length === 0);
|
|
101
|
+
});
|
|
85
102
|
});
|
package/src/File.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as fs from 'node:fs';
|
|
2
2
|
import * as path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { Readable } from 'node:stream';
|
|
4
|
+
import { finished } from 'node:stream/promises';
|
|
5
|
+
import { writeToStream, parseStream } from 'fast-csv';
|
|
4
6
|
import { snapshot } from './snapshot.js';
|
|
5
7
|
|
|
6
8
|
/**
|
|
@@ -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
|
}
|
|
@@ -29,9 +27,28 @@ export class File {
|
|
|
29
27
|
return this.exists ? fs.readFileSync(this.path, 'utf8') : undefined;
|
|
30
28
|
}
|
|
31
29
|
|
|
32
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @returns lines as strings, removes trailing '\n'
|
|
32
|
+
*/
|
|
33
|
+
lines() {
|
|
34
|
+
const contents = (this.read() || '').split('\n');
|
|
35
|
+
return contents.slice(0, contents.length - 1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get readStream() {
|
|
39
|
+
return this.exists ? fs.createReadStream(this.path) : Readable.from([]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
get writeStream() {
|
|
33
43
|
fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
|
|
34
|
-
fs.
|
|
44
|
+
return fs.createWriteStream(this.path);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
write(contents: string | ReadableStream) {
|
|
48
|
+
fs.mkdirSync(path.parse(this.path).dir, { recursive: true });
|
|
49
|
+
if (typeof contents === 'string') return fs.writeFileSync(this.path, contents);
|
|
50
|
+
if (contents instanceof ReadableStream) return finished(Readable.from(contents).pipe(this.writeStream));
|
|
51
|
+
throw new Error(`Invalid content type: ${typeof contents}`);
|
|
35
52
|
}
|
|
36
53
|
|
|
37
54
|
/**
|
|
@@ -44,14 +61,6 @@ export class File {
|
|
|
44
61
|
fs.appendFileSync(this.path, contents + '\n');
|
|
45
62
|
}
|
|
46
63
|
|
|
47
|
-
/**
|
|
48
|
-
* @returns lines as strings, removes trailing '\n'
|
|
49
|
-
*/
|
|
50
|
-
lines() {
|
|
51
|
-
const contents = (this.read() || '').split('\n');
|
|
52
|
-
return contents.slice(0, contents.length - 1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
64
|
static get FileType() {
|
|
56
65
|
return FileType;
|
|
57
66
|
}
|
|
@@ -83,32 +92,34 @@ export class File {
|
|
|
83
92
|
}
|
|
84
93
|
}
|
|
85
94
|
|
|
86
|
-
|
|
95
|
+
/**
|
|
96
|
+
* A generic file adaptor, extended by specific file type implementations
|
|
97
|
+
*/
|
|
98
|
+
export class FileType {
|
|
87
99
|
file;
|
|
88
100
|
|
|
89
|
-
constructor(filepath: string, contents?:
|
|
101
|
+
constructor(filepath: string, contents?: string) {
|
|
90
102
|
this.file = new File(filepath);
|
|
91
|
-
if (contents)
|
|
92
|
-
if (typeof contents !== 'string') {
|
|
93
|
-
throw new Error('File contents must be a string');
|
|
94
|
-
}
|
|
95
|
-
this.file.write(contents);
|
|
96
|
-
}
|
|
103
|
+
if (contents) this.file.write(contents);
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
get exists() {
|
|
100
107
|
return this.file.exists;
|
|
101
108
|
}
|
|
102
109
|
|
|
103
|
-
delete() {
|
|
104
|
-
this.file.delete();
|
|
105
|
-
}
|
|
106
|
-
|
|
107
110
|
get path() {
|
|
108
111
|
return this.file.path;
|
|
109
112
|
}
|
|
113
|
+
|
|
114
|
+
delete() {
|
|
115
|
+
this.file.delete();
|
|
116
|
+
}
|
|
110
117
|
}
|
|
111
118
|
|
|
119
|
+
/**
|
|
120
|
+
* A .json file that maintains data type when reading/writing.
|
|
121
|
+
* This is unsafe! Type is not checked at runtime, avoid using on files manipulated outside of your application.
|
|
122
|
+
*/
|
|
112
123
|
export class FileTypeJson<T> extends FileType {
|
|
113
124
|
constructor(filepath: string, contents?: T) {
|
|
114
125
|
super(filepath.endsWith('.json') ? filepath : filepath + '.json');
|
|
@@ -125,6 +136,10 @@ export class FileTypeJson<T> extends FileType {
|
|
|
125
136
|
}
|
|
126
137
|
}
|
|
127
138
|
|
|
139
|
+
/**
|
|
140
|
+
* New-line delimited json file (.ndjson)
|
|
141
|
+
* @see https://jsonltools.com/ndjson-format-specification
|
|
142
|
+
*/
|
|
128
143
|
export class FileTypeNdjson<T extends object> extends FileType {
|
|
129
144
|
constructor(filepath: string, lines?: T | T[]) {
|
|
130
145
|
super(filepath.endsWith('.ndjson') ? filepath : filepath + '.ndjson');
|
|
@@ -133,7 +148,7 @@ export class FileTypeNdjson<T extends object> extends FileType {
|
|
|
133
148
|
|
|
134
149
|
append(lines: T | T[]) {
|
|
135
150
|
this.file.append(
|
|
136
|
-
Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines))
|
|
151
|
+
Array.isArray(lines) ? lines.map((l) => JSON.stringify(snapshot(l))) : JSON.stringify(snapshot(lines)),
|
|
137
152
|
);
|
|
138
153
|
}
|
|
139
154
|
|
|
@@ -144,6 +159,10 @@ export class FileTypeNdjson<T extends object> extends FileType {
|
|
|
144
159
|
|
|
145
160
|
type Key<T extends object> = keyof T;
|
|
146
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Comma separated values (.csv).
|
|
164
|
+
* Input rows as objects, keys are used as column headers
|
|
165
|
+
*/
|
|
147
166
|
export class FileTypeCsv<Row extends object> extends FileType {
|
|
148
167
|
constructor(filepath: string) {
|
|
149
168
|
super(filepath.endsWith('.csv') ? filepath : filepath + '.csv');
|
|
@@ -160,36 +179,34 @@ export class FileTypeCsv<Row extends object> extends FileType {
|
|
|
160
179
|
}
|
|
161
180
|
const headers = Array.from(headerSet);
|
|
162
181
|
const outRows = rows.map((row) => headers.map((key) => row[key]));
|
|
163
|
-
|
|
164
|
-
|
|
182
|
+
return finished(writeToStream(this.file.writeStream, [headers, ...outRows]));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
#parseVal(val: string) {
|
|
186
|
+
if (val.toLowerCase() === 'false') return false;
|
|
187
|
+
if (val.toLowerCase() === 'true') return true;
|
|
188
|
+
if (val.length === 0) return null;
|
|
189
|
+
if (/^[\.0-9]+$/.test(val)) return Number(val);
|
|
190
|
+
return val;
|
|
165
191
|
}
|
|
166
192
|
|
|
167
193
|
async read() {
|
|
168
194
|
return new Promise<Row[]>((resolve, reject) => {
|
|
169
195
|
const parsed: Row[] = [];
|
|
170
|
-
|
|
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 })
|
|
196
|
+
parseStream(this.file.readStream, { headers: true })
|
|
180
197
|
.on('error', (e) => reject(e))
|
|
181
|
-
.on('end', () => resolve(parsed))
|
|
182
198
|
.on('data', (raw: Record<Key<Row>, string>) => {
|
|
183
199
|
parsed.push(
|
|
184
200
|
Object.entries(raw).reduce(
|
|
185
201
|
(all, [key, val]) => ({
|
|
186
202
|
...all,
|
|
187
|
-
[key]: parseVal(val as string),
|
|
203
|
+
[key]: this.#parseVal(val as string),
|
|
188
204
|
}),
|
|
189
|
-
{} as Row
|
|
190
|
-
)
|
|
205
|
+
{} as Row,
|
|
206
|
+
),
|
|
191
207
|
);
|
|
192
|
-
})
|
|
208
|
+
})
|
|
209
|
+
.on('end', () => resolve(parsed));
|
|
193
210
|
});
|
|
194
211
|
}
|
|
195
212
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,7 +2,6 @@ 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
4
|
export { File, FileType, FileTypeJson, FileTypeNdjson, FileTypeCsv } from './File.js';
|
|
5
|
-
export { Jwt } from './Jwt.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
|
-
});
|
package/src/Jwt.ts
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import { default as jsonwebtoken, type JwtPayload, type SignOptions } from 'jsonwebtoken';
|
|
2
|
-
import { merge } from 'lodash-es';
|
|
3
|
-
|
|
4
|
-
type JwtConfig = {
|
|
5
|
-
payload: JwtPayload;
|
|
6
|
-
options: SignOptions;
|
|
7
|
-
seconds: number;
|
|
8
|
-
key: string;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export class Jwt {
|
|
12
|
-
config;
|
|
13
|
-
#saved?: {
|
|
14
|
-
exp: number;
|
|
15
|
-
token: string;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
constructor(config: JwtConfig) {
|
|
19
|
-
this.config = config;
|
|
20
|
-
this.#createToken();
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get now() {
|
|
24
|
-
return Math.floor(Date.now() / 1000);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
#createToken() {
|
|
28
|
-
const exp = this.now + this.config.seconds;
|
|
29
|
-
const payload: JwtPayload = merge(
|
|
30
|
-
{
|
|
31
|
-
iat: this.now,
|
|
32
|
-
exp,
|
|
33
|
-
},
|
|
34
|
-
this.config.payload
|
|
35
|
-
);
|
|
36
|
-
const token = jsonwebtoken.sign(payload, this.config.key, this.config.options);
|
|
37
|
-
this.#saved = { token, exp };
|
|
38
|
-
return token;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
get token() {
|
|
42
|
-
if (this.#saved && this.#saved.exp > this.now) {
|
|
43
|
-
return this.#saved.token;
|
|
44
|
-
}
|
|
45
|
-
return this.#createToken();
|
|
46
|
-
}
|
|
47
|
-
}
|