backlib 0.3.3 → 0.5.0-SNAPSHOT.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
@@ -8,7 +8,7 @@ Minimalist utilities for backend services.
8
8
  - **Web Async** Web request utilities based on [koajs](https://koajs.com/) over express as it is a modern rewrite of more or less the same API with backed in support for Promise/async/await (simplify many of the usecases)
9
9
 
10
10
 
11
- > NOTE: Still under development. If not part of BriteSnow, wait for 0.3.0 and above to use this library. In the meantime feel free to copy/paste code.
11
+ > NOTE: Version `0.4.0` and above uses ESM module (i.e., `type: module`) only.
12
12
 
13
13
 
14
14
  ## Log
@@ -1,19 +1,17 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.newLeafTracer = void 0;
4
1
  /**
5
2
  * Tracker to determine if a decorator is on the leaf"est" method of the class hierarchy.
6
3
  */
7
- function newLeafTracer() {
4
+ export function newLeafTracer() {
8
5
  return new LeafTracer();
9
6
  }
10
- exports.newLeafTracer = newLeafTracer;
11
7
  /**
12
8
  * Ter
13
9
  */
14
10
  class LeafTracer {
15
- // object class, by TopTarget
16
- dic = new Map();
11
+ constructor() {
12
+ // object class, by TopTarget
13
+ this.dic = new Map();
14
+ }
17
15
  /** Returns true if this method is the leaf most method annotatio traced by this tracer */
18
16
  trace(objectClass, targetClass, propertyKey) {
19
17
  let topTargetClassByProperty = this.dic.get(objectClass);
@@ -41,3 +39,4 @@ class LeafTracer {
41
39
  }
42
40
  }
43
41
  }
42
+ //# sourceMappingURL=decorator-leaf-tracer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decorator-leaf-tracer.js","sourceRoot":"","sources":["../src/decorator-leaf-tracer.ts"],"names":[],"mappings":"AACA;;GAEG;AACH,MAAM,UAAU,aAAa;IAC5B,OAAO,IAAI,UAAU,EAAE,CAAC;AACzB,CAAC;AAID;;GAEG;AACH,MAAM,UAAU;IAAhB;QAEC,6BAA6B;QACrB,QAAG,GAA6C,IAAI,GAAG,EAAE,CAAC;IAgCnE,CAAC;IA9BA,0FAA0F;IAC1F,KAAK,CAAC,WAAqB,EAAE,WAAqB,EAAE,WAAmB;QAEtE,IAAI,wBAAwB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAEzD,6FAA6F;QAC7F,oCAAoC;QACpC,IAAI,CAAC,wBAAwB,EAAE;YAC9B,wBAAwB,GAAG,IAAI,GAAG,EAAoB,CAAC;YACvD,wBAAwB,CAAC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;YAEvD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;YACpD,OAAO,IAAI,CAAC;SACZ;QACD,+BAA+B;aAC1B;YACJ,IAAI,cAAc,GAAG,wBAAwB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YAE/D,8DAA8D;YAC9D,6CAA6C;YAC7C,IAAI,CAAC,cAAc,EAAE;gBACpB,wBAAwB,CAAC,GAAG,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;gBACvD,OAAO,IAAI,CAAC;aACZ;YACD,6EAA6E;iBACxE;gBACJ,OAAO,CAAC,cAAc,KAAK,WAAW,CAAC,CAAC;aACxC;SACD;IACF,CAAC;CACD"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- export * from './decorator-leaf-tracer';
2
- export * from './fs';
3
- export * from './log';
4
- export * from './utils';
1
+ export { newLeafTracer } from './decorator-leaf-tracer.js';
2
+ export { FileWriter } from './log-file-writer.js';
3
+ export type { FileNameProvider, FileWriterOptions, OnFileCompleted, RecordSerializer } from './log-file-writer.js';
4
+ export { BaseLog } from './log.js';
5
+ export type { LogOptions, LogWriter } from './log.js';
6
+ export { prompt } from './utils.js';
package/dist/index.js CHANGED
@@ -1,16 +1,9 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- __exportStar(require("./decorator-leaf-tracer"), exports);
14
- __exportStar(require("./fs"), exports);
15
- __exportStar(require("./log"), exports);
16
- __exportStar(require("./utils"), exports);
1
+ // Export decorator constructs
2
+ export { newLeafTracer } from './decorator-leaf-tracer.js';
3
+ // Export log-file-writer constructs
4
+ export { FileWriter } from './log-file-writer.js';
5
+ // Export log constructs
6
+ export { BaseLog } from './log.js';
7
+ // Export utils constructs
8
+ export { prompt } from './utils.js';
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8BAA8B;AAC9B,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,oCAAoC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAElD,wBAAwB;AACxB,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,0BAA0B;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { LogWriter } from './index.js';
2
+ export declare type OnFileCompleted = (file: string) => Promise<void>;
3
+ export declare type FileNameProvider = (rev: number) => string;
4
+ /** Record serializer to string, which will be appended to the file. If null, record will be skipped */
5
+ export declare type RecordSerializer<R> = (rec: R) => string | null;
6
+ export interface FileWriterOptions<R> {
7
+ /** Local directory in which the logs files will be saved */
8
+ dir: string;
9
+ /** maxCount of record before file is uploaded to destination */
10
+ maxCount: number;
11
+ /** max time (in seconds) before file is uploaded to destination (which ever comes first with maxCount) */
12
+ maxTime: number;
13
+ /** Optional fileName generator for the new log file name (MUST BE UNIQUE for this dir) */
14
+ fileNameProvider?: FileNameProvider;
15
+ recordSerializer?: RecordSerializer<R>;
16
+ onFileCompleted?: OnFileCompleted;
17
+ }
18
+ export declare class FileWriter<R> implements LogWriter<R> {
19
+ readonly name = "to-deprecate";
20
+ private dir;
21
+ private maxCount;
22
+ private maxTime;
23
+ private fileNameProvider;
24
+ private recordSerializer;
25
+ private onFileCompleted?;
26
+ private _init;
27
+ private _rev;
28
+ private count;
29
+ private nextUpload;
30
+ private lastUpload?;
31
+ private file?;
32
+ constructor(opts: FileWriterOptions<R>);
33
+ private init;
34
+ /** Update the revision file */
35
+ private rev;
36
+ /** IMPLEMENTATION of the FileWriter interface */
37
+ writeRec(rec: R): Promise<void>;
38
+ private endFile;
39
+ }
@@ -0,0 +1,104 @@
1
+ import { pathExists } from 'fs-aux';
2
+ import { appendFile, mkdir, rename } from 'fs/promises';
3
+ import * as Path from "path";
4
+ import { isString } from 'utils-min';
5
+ export class FileWriter {
6
+ constructor(opts) {
7
+ this.name = 'to-deprecate'; // TODO: to deprecate
8
+ this._init = false;
9
+ this._rev = 0;
10
+ this.count = 0;
11
+ this.nextUpload = null; // null means nothing scheduled
12
+ this.maxCount = opts.maxCount;
13
+ this.maxTime = opts.maxTime;
14
+ this.dir = opts.dir;
15
+ this.fileNameProvider = opts.fileNameProvider ?? defaultFileNameProvider;
16
+ this.onFileCompleted = opts.onFileCompleted;
17
+ this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
18
+ }
19
+ async init() {
20
+ if (!this._init) {
21
+ await mkdir(this.dir, { recursive: true });
22
+ this.rev();
23
+ this._init = true;
24
+ }
25
+ }
26
+ /** Update the revision file */
27
+ rev() {
28
+ this.count = 0;
29
+ this._rev = this._rev + 1;
30
+ const fileName = this.fileNameProvider(this._rev);
31
+ this.file = Path.join(this.dir, fileName);
32
+ }
33
+ /** IMPLEMENTATION of the FileWriter interface */
34
+ async writeRec(rec) {
35
+ if (!this._init) {
36
+ await this.init();
37
+ }
38
+ const str = this.recordSerializer(rec);
39
+ if (str != null) {
40
+ const strWithNl = str + '\n'; // add the new line
41
+ await appendFile(this.file, strWithNl);
42
+ }
43
+ // add count
44
+ this.count = this.count + 1;
45
+ // if we are above the count, we upload
46
+ if (this.count > this.maxCount) {
47
+ console.log(`->> rev ${this.name} because count ${this.count} > maxCount ${this.maxCount}`);
48
+ await this.endFile();
49
+ }
50
+ // if still below the count, but do not have this.nextUpload, schedule one
51
+ else if (this.nextUpload === null) {
52
+ const maxTimeMs = this.maxTime * 1000; // in ms
53
+ const nextUpload = Date.now() + maxTimeMs;
54
+ this.nextUpload = nextUpload;
55
+ setTimeout(async () => {
56
+ // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
57
+ if (this.nextUpload === nextUpload) {
58
+ console.log(`->> rev ${this.name} because maxTimeMs ${maxTimeMs}`);
59
+ await this.endFile();
60
+ }
61
+ }, maxTimeMs);
62
+ }
63
+ }
64
+ async endFile() {
65
+ const file = this.file;
66
+ // we rev just before to make sure other logs will happen on new files
67
+ this.rev();
68
+ try {
69
+ const exists = await pathExists(file);
70
+ if (exists) {
71
+ if (this.onFileCompleted) {
72
+ try {
73
+ console.log(`->> endFile processing ${this.name} `);
74
+ await this.onFileCompleted(file);
75
+ }
76
+ catch (ex) {
77
+ console.log(`LOG PROCESSING ERROR - processing file '${file}' caused the following error: ${ex}`);
78
+ }
79
+ }
80
+ }
81
+ else {
82
+ console.log(`LOG PROCESSING REMOVE ERROR - cannot be processed file '${file}' does not exists anymore`);
83
+ }
84
+ }
85
+ // Note: note sure we need this global catch now.
86
+ catch (ex) {
87
+ console.log(`LOG PROCESSING - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
88
+ await rename(file, file + '.error');
89
+ }
90
+ this.count = 0;
91
+ this.lastUpload = Date.now();
92
+ this.nextUpload = null;
93
+ }
94
+ }
95
+ /** default serializer */
96
+ function defaultSerializer(rec) {
97
+ return isString(rec) ? rec : JSON.stringify(rec);
98
+ }
99
+ function defaultFileNameProvider(rev) {
100
+ const date = new Date().toISOString().replace(/[T:.]/g, "-").slice(0, -1);
101
+ const revStr = `${rev}`.padStart(5, '0');
102
+ return `log-file-${date}-${revStr}.log`;
103
+ }
104
+ //# sourceMappingURL=log-file-writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-file-writer.js","sourceRoot":"","sources":["../src/log-file-writer.ts"],"names":[],"mappings":"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"}
package/dist/log.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- interface LogOptions<R> {
1
+ export interface LogOptions<R> {
2
2
  writers: LogWriter<R>[];
3
3
  }
4
4
  /**
@@ -10,44 +10,5 @@ export declare class BaseLog<R> {
10
10
  log(rec: R): Promise<void>;
11
11
  }
12
12
  export interface LogWriter<R> {
13
- readonly name: string;
14
13
  writeRec?(rec: R): Promise<void>;
15
14
  }
16
- /** processing file, if return true, then file will be assumed to be full processed, and will be deleted */
17
- export declare type FileProcessor = (file: string) => Promise<boolean>;
18
- /** Record serializer to string, which will be appended to the file. If null, record will be skipped */
19
- export declare type RecordSerializer<R> = (rec: R) => string | null;
20
- interface FileLogWriterOptions<R> {
21
- /** name of the logWriter, will be used as prefix */
22
- name: string;
23
- /** Local directory in which the logs files will be saved */
24
- dir: string;
25
- /** maxCount of record before file is uploaded to destination */
26
- maxCount: number;
27
- /** max time (in ms) before file is uploaded to destination (which ever comes first with maxCount) */
28
- maxTime: number;
29
- fileProcessor?: FileProcessor;
30
- recordSerializer?: RecordSerializer<R>;
31
- }
32
- export declare class FileLogWriter<R> implements LogWriter<R> {
33
- readonly name: string;
34
- private dir;
35
- private maxCount;
36
- private maxTime;
37
- private fileProcessor?;
38
- private recordSerializer;
39
- private _init;
40
- private _rev;
41
- private count;
42
- private nextUpload;
43
- private lastUpload?;
44
- private file?;
45
- constructor(opts: FileLogWriterOptions<R>);
46
- private init;
47
- /** Update the revision file */
48
- private rev;
49
- /** IMPLEMENTATION of the FileWriter interface */
50
- writeRec(rec: R): Promise<void>;
51
- private endFile;
52
- }
53
- export {};
package/dist/log.js CHANGED
@@ -1,35 +1,10 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
- Object.defineProperty(o, "default", { enumerable: true, value: v });
11
- }) : function(o, v) {
12
- o["default"] = v;
13
- });
14
- var __importStar = (this && this.__importStar) || function (mod) {
15
- if (mod && mod.__esModule) return mod;
16
- var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
- __setModuleDefault(result, mod);
19
- return result;
20
- };
21
- Object.defineProperty(exports, "__esModule", { value: true });
22
- exports.FileLogWriter = exports.BaseLog = void 0;
23
- const fs = __importStar(require("fs-extra"));
24
- const Path = __importStar(require("path"));
25
- const utils_min_1 = require("utils-min");
26
- const fs_1 = require("./fs");
1
+ // const { pathExists, mkdirs, appendFile, rename } = (await import('fs-extra')).default;
27
2
  /**
28
3
  * Base Log class that handle the base log management logic.
29
4
  */
30
- class BaseLog {
31
- logWriters = [];
5
+ export class BaseLog {
32
6
  constructor(opts) {
7
+ this.logWriters = [];
33
8
  this.logWriters = [...opts.writers];
34
9
  }
35
10
  async log(rec) {
@@ -41,110 +16,10 @@ class BaseLog {
41
16
  }
42
17
  catch (ex) {
43
18
  // here log console.log, no choise
44
- console.log(`LOG ERROR - Log exception when writeRec on logWriter ${writer.name}. ${ex}`);
19
+ console.log(`ERROR - BACKLIB - Log exception when writeRec on logWriter ${writer}. ${ex}`);
45
20
  }
46
21
  }
47
22
  }
48
23
  }
49
24
  }
50
- exports.BaseLog = BaseLog;
51
- class FileLogWriter {
52
- name;
53
- dir;
54
- maxCount;
55
- maxTime;
56
- fileProcessor;
57
- recordSerializer;
58
- _init = false;
59
- _rev = 0;
60
- count = 0;
61
- nextUpload = null; // null means nothing scheduled
62
- lastUpload;
63
- file;
64
- constructor(opts) {
65
- this.name = opts.name;
66
- this.maxCount = opts.maxCount;
67
- this.maxTime = opts.maxTime;
68
- this.dir = opts.dir;
69
- this.fileProcessor = opts.fileProcessor;
70
- this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
71
- }
72
- async init() {
73
- if (!this._init) {
74
- await fs.mkdirs(this.dir);
75
- // delete the logs dir if exit
76
- const oldLogFiles = await fs_1.glob(this.dir + `${this.name}*.log`);
77
- await fs_1.saferRemove(oldLogFiles);
78
- console.log('Deleted old log files', oldLogFiles);
79
- this.rev();
80
- this._init = true;
81
- }
82
- }
83
- /** Update the revision file */
84
- rev() {
85
- this.count = 0;
86
- this._rev = this._rev + 1;
87
- const suffix = `${this._rev}`.padStart(5, '0');
88
- this.file = Path.join(this.dir, `${this.name}-${suffix}.log`);
89
- }
90
- /** IMPLEMENTATION of the FileWriter interface */
91
- async writeRec(rec) {
92
- if (!this._init) {
93
- await this.init();
94
- }
95
- // TODO: Need to move this outside of the generic log implementation (we do this because bigquery expect info to be string, since it can be dynamic)
96
- // NOTE: In fact, this whole file write and upload, should be part of a FileLogWriter, and we just treat it as above (perhaps in the BigQueryLogWriter extends FileLogWriter)
97
- const str = this.recordSerializer(rec);
98
- if (str != null) {
99
- await fs.appendFile(this.file, str);
100
- }
101
- // add count
102
- this.count = this.count + 1;
103
- // if we are above the count, we upload
104
- if (this.count > this.maxCount) {
105
- await this.endFile();
106
- }
107
- // if still below the count, but do not have this.nextUpload, schedule one
108
- else if (this.nextUpload === null) {
109
- const maxTimeMs = this.maxTime * 1000; // in ms
110
- const nextUpload = Date.now() + maxTimeMs;
111
- this.nextUpload = nextUpload;
112
- setTimeout(async () => {
113
- // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
114
- if (this.nextUpload === nextUpload) {
115
- await this.endFile();
116
- }
117
- }, maxTimeMs);
118
- }
119
- }
120
- async endFile() {
121
- const file = this.file;
122
- // we rev just before to make sure other logs will happen on new files
123
- this.rev();
124
- try {
125
- const exists = await fs.pathExists(file);
126
- if (exists) {
127
- if (this.fileProcessor) {
128
- await this.fileProcessor(file);
129
- }
130
- await fs_1.saferRemove(file);
131
- }
132
- else {
133
- console.log(`CODE ERROR - can't upload to big query ${file} does not exists`);
134
- }
135
- }
136
- catch (ex) {
137
- console.log(`ERROR - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
138
- await fs.rename(file, file + '.error');
139
- }
140
- this.count = 0;
141
- this.lastUpload = Date.now();
142
- this.nextUpload = null;
143
- }
144
- }
145
- exports.FileLogWriter = FileLogWriter;
146
- /** default serializer */
147
- function defaultSerializer(rec) {
148
- return utils_min_1.isString(rec) ? rec : JSON.stringify(rec);
149
- }
150
- //#endregion ---------- /FileLogWriter ----------
25
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AACA,yFAAyF;AAQzF;;GAEG;AACH,MAAM,OAAO,OAAO;IAGnB,YAAY,IAAmB;QAFvB,eAAU,GAAmB,EAAE,CAAC;QAGvC,IAAI,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAM;QAEf,GAAG;QACH,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,UAAU,EAAE;YACrC,IAAI,MAAM,CAAC,QAAQ,EAAE;gBACpB,IAAI;oBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;iBAC3B;gBAAC,OAAO,EAAE,EAAE;oBACZ,kCAAkC;oBAClC,OAAO,CAAC,GAAG,CAAC,8DAA8D,MAAM,KAAK,EAAE,EAAE,CAAC,CAAC;iBAC3F;aACD;SACD;IAEF,CAAC;CAED"}
package/dist/utils.js CHANGED
@@ -1,8 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.prompt = void 0;
4
1
  /** Promise a message and return the trimmed entered value */
5
- async function prompt(message) {
2
+ export async function prompt(message) {
6
3
  process.stdout.write(`\n${message}: `);
7
4
  return new Promise(function (resolve, reject) {
8
5
  process.stdin.resume();
@@ -13,4 +10,4 @@ async function prompt(message) {
13
10
  });
14
11
  });
15
12
  }
16
- exports.prompt = prompt;
13
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,6DAA6D;AAC7D,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,OAAe;IAC3C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,OAAO,IAAI,CAAC,CAAC;IACvC,OAAO,IAAI,OAAO,CAAC,UAAU,OAAO,EAAE,MAAM;QAC3C,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,IAAI;YACtC,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,35 +1,30 @@
1
1
  {
2
2
  "name": "backlib",
3
- "version": "0.3.3",
3
+ "type": "module",
4
+ "version": "0.5.0-SNAPSHOT.1",
4
5
  "description": "Minimalist library for backend services",
5
6
  "main": "dist/index.js",
6
- "typings": "dist/index.d.ts",
7
7
  "engines": {
8
- "node": ">=14.4"
8
+ "node": ">=16.13"
9
9
  },
10
10
  "repository": {
11
11
  "type": "git",
12
12
  "url": "https://github.com/britesnow/node-backlib.git"
13
13
  },
14
14
  "scripts": {
15
- "test": "TS_NODE_FILES=true ./node_modules/.bin/mocha -p ./test/tsconfig.json ./test/spec/*.spec.ts",
16
- "testw": "TS_NODE_FILES=true ./node_modules/.bin/mocha -p ./test/tsconfig.json ./test/spec/*.spec.ts --watch --watch-files './test/**/*.ts','./src/**/*.ts'",
15
+ "test": "echo 'no test for now'",
17
16
  "prepare": "node_modules/.bin/rimraf ./dist && ./node_modules/.bin/tsc"
18
17
  },
19
18
  "author": "jeremy.chone@gmail.com",
20
19
  "license": "MIT",
21
20
  "dependencies": {
22
- "fast-glob": "^3.2.7",
23
- "fs-extra": "^10.0.0",
24
- "utils-min": "^0.1.18"
21
+ "fs-aux": "^0.1.0",
22
+ "utils-min": "^0.2.0"
25
23
  },
26
24
  "devDependencies": {
27
- "@types/fs-extra": "^9.0.12",
28
- "@types/mocha": "^9.0.0",
29
- "mocha": "^9.0.2",
25
+ "@types/node": "^17.0.10",
30
26
  "rimraf": "^3.0.2",
31
- "ts-node-dev": "^1.1.8",
32
- "typescript": "^4.3.5"
27
+ "typescript": "^4.5.5"
33
28
  },
34
29
  "files": [
35
30
  "src/",
package/src/index.ts CHANGED
@@ -1,5 +1,19 @@
1
- export * from './decorator-leaf-tracer';
2
- export * from './fs';
3
- export * from './log';
4
- export * from './utils';
1
+ // Export decorator constructs
2
+ export { newLeafTracer } from './decorator-leaf-tracer.js';
3
+ // Export log-file-writer constructs
4
+ export { FileWriter } from './log-file-writer.js';
5
+ export type { FileNameProvider, FileWriterOptions, OnFileCompleted, RecordSerializer } from './log-file-writer.js';
6
+ // Export log constructs
7
+ export { BaseLog } from './log.js';
8
+ export type { LogOptions, LogWriter } from './log.js';
9
+ // Export utils constructs
10
+ export { prompt } from './utils.js';
11
+
12
+
13
+
14
+
15
+
16
+
17
+
18
+
5
19
 
@@ -0,0 +1,159 @@
1
+ import { pathExists } from 'fs-aux';
2
+ import { appendFile, mkdir, rename } from 'fs/promises';
3
+ import * as Path from "path";
4
+ import { isString } from 'utils-min';
5
+ import { LogWriter } from './index.js';
6
+
7
+ export type OnFileCompleted = (file: string) => Promise<void>;
8
+ export type FileNameProvider = (rev: number) => string;
9
+
10
+ /** Record serializer to string, which will be appended to the file. If null, record will be skipped */
11
+ export type RecordSerializer<R> = (rec: R) => string | null;
12
+
13
+ export interface FileWriterOptions<R> {
14
+ /** Local directory in which the logs files will be saved */
15
+ dir: string;
16
+ /** maxCount of record before file is uploaded to destination */
17
+ maxCount: number;
18
+ /** max time (in seconds) before file is uploaded to destination (which ever comes first with maxCount) */
19
+ maxTime: number;
20
+
21
+ /** Optional fileName generator for the new log file name (MUST BE UNIQUE for this dir) */
22
+ fileNameProvider?: FileNameProvider,
23
+ /* Optional recordSerializer to file. By default, JSON.serializer() (new line json) */
24
+ recordSerializer?: RecordSerializer<R>;
25
+ /* Call when a log file is completed (i.e., new entries will go to another file) */
26
+ onFileCompleted?: OnFileCompleted;
27
+
28
+ }
29
+
30
+ export class FileWriter<R> implements LogWriter<R> {
31
+ readonly name = 'to-deprecate'; // TODO: to deprecate
32
+ private dir: string;
33
+ private maxCount: number;
34
+ private maxTime: number;
35
+
36
+ private fileNameProvider: FileNameProvider;
37
+ private recordSerializer: RecordSerializer<R>;
38
+ private onFileCompleted?: OnFileCompleted;
39
+
40
+ private _init = false;
41
+ private _rev = 0;
42
+ private count = 0;
43
+ private nextUpload: number | null = null; // null means nothing scheduled
44
+ private lastUpload?: number;
45
+
46
+ private file?: string;
47
+
48
+ constructor(opts: FileWriterOptions<R>) {
49
+ this.maxCount = opts.maxCount;
50
+ this.maxTime = opts.maxTime;
51
+ this.dir = opts.dir;
52
+ this.fileNameProvider = opts.fileNameProvider ?? defaultFileNameProvider;
53
+ this.onFileCompleted = opts.onFileCompleted;
54
+ this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
55
+ }
56
+
57
+ private async init() {
58
+ if (!this._init) {
59
+ await mkdir(this.dir, { recursive: true });
60
+ this.rev();
61
+ this._init = true;
62
+ }
63
+ }
64
+
65
+ /** Update the revision file */
66
+ private rev() {
67
+ this.count = 0;
68
+ this._rev = this._rev + 1;
69
+
70
+ const fileName = this.fileNameProvider(this._rev);
71
+
72
+ this.file = Path.join(this.dir, fileName)
73
+ }
74
+
75
+
76
+ /** IMPLEMENTATION of the FileWriter interface */
77
+ async writeRec(rec: R) {
78
+
79
+ if (!this._init) {
80
+ await this.init();
81
+ }
82
+
83
+ const str = this.recordSerializer(rec);
84
+ if (str != null) {
85
+ const strWithNl = str + '\n'; // add the new line
86
+ await appendFile(this.file!, strWithNl);
87
+ }
88
+
89
+ // add count
90
+ this.count = this.count + 1;
91
+
92
+ // if we are above the count, we upload
93
+ if (this.count > this.maxCount) {
94
+ console.log(`->> rev ${this.name} because count ${this.count} > maxCount ${this.maxCount}`);
95
+ await this.endFile();
96
+ }
97
+ // if still below the count, but do not have this.nextUpload, schedule one
98
+ else if (this.nextUpload === null) {
99
+ const maxTimeMs = this.maxTime * 1000; // in ms
100
+
101
+ const nextUpload = Date.now() + maxTimeMs;
102
+ this.nextUpload = nextUpload;
103
+
104
+ setTimeout(async () => {
105
+ // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
106
+ if (this.nextUpload === nextUpload) {
107
+ console.log(`->> rev ${this.name} because maxTimeMs ${maxTimeMs}`);
108
+ await this.endFile();
109
+ }
110
+ }, maxTimeMs);
111
+ }
112
+
113
+ }
114
+
115
+ private async endFile() {
116
+ const file = this.file!;
117
+ // we rev just before to make sure other logs will happen on new files
118
+ this.rev();
119
+
120
+ try {
121
+ const exists = await pathExists(file);
122
+ if (exists) {
123
+ if (this.onFileCompleted) {
124
+ try {
125
+ console.log(`->> endFile processing ${this.name} `);
126
+ await this.onFileCompleted(file);
127
+ } catch (ex: any) {
128
+ console.log(`LOG PROCESSING ERROR - processing file '${file}' caused the following error: ${ex}`);
129
+ }
130
+ }
131
+ } else {
132
+ console.log(`LOG PROCESSING REMOVE ERROR - cannot be processed file '${file}' does not exists anymore`);
133
+ }
134
+
135
+ }
136
+ // Note: note sure we need this global catch now.
137
+ catch (ex: any) {
138
+ console.log(`LOG PROCESSING - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
139
+ await rename(file, file + '.error');
140
+ }
141
+
142
+ this.count = 0;
143
+ this.lastUpload = Date.now();
144
+ this.nextUpload = null;
145
+ }
146
+
147
+ }
148
+
149
+ /** default serializer */
150
+
151
+ function defaultSerializer<R>(rec: R): string {
152
+ return isString(rec) ? rec : JSON.stringify(rec);
153
+ }
154
+
155
+ function defaultFileNameProvider(rev: number): string {
156
+ const date = new Date().toISOString().replace(/[T:.]/g, "-").slice(0, -1);
157
+ const revStr = `${rev}`.padStart(5, '0');
158
+ return `log-file-${date}-${revStr}.log`;
159
+ }
package/src/log.ts CHANGED
@@ -1,11 +1,9 @@
1
- import * as fs from 'fs-extra';
2
- import * as Path from 'path';
3
- import { isString } from 'utils-min';
4
- import { glob, saferRemove } from './fs';
1
+
2
+ // const { pathExists, mkdirs, appendFile, rename } = (await import('fs-extra')).default;
5
3
 
6
4
 
7
5
  //#region ---------- BaseLog ----------
8
- interface LogOptions<R> {
6
+ export interface LogOptions<R> {
9
7
  writers: LogWriter<R>[]
10
8
  }
11
9
 
@@ -28,7 +26,7 @@ export class BaseLog<R> {
28
26
  await writer.writeRec(rec);
29
27
  } catch (ex) {
30
28
  // here log console.log, no choise
31
- console.log(`LOG ERROR - Log exception when writeRec on logWriter ${writer.name}. ${ex}`);
29
+ console.log(`ERROR - BACKLIB - Log exception when writeRec on logWriter ${writer}. ${ex}`);
32
30
  }
33
31
  }
34
32
  }
@@ -39,161 +37,6 @@ export class BaseLog<R> {
39
37
  //#endregion ---------- /BaseLog ----------
40
38
 
41
39
  export interface LogWriter<R> {
42
- readonly name: string;
43
40
  writeRec?(rec: R): Promise<void>
44
41
  }
45
42
 
46
-
47
- //#region ---------- FileLogWriter ----------
48
- /** processing file, if return true, then file will be assumed to be full processed, and will be deleted */
49
- export type FileProcessor = (file: string) => Promise<boolean>;
50
-
51
- /** Record serializer to string, which will be appended to the file. If null, record will be skipped */
52
- export type RecordSerializer<R> = (rec: R) => string | null;
53
-
54
- interface FileLogWriterOptions<R> {
55
- /** name of the logWriter, will be used as prefix */
56
- name: string;
57
- /** Local directory in which the logs files will be saved */
58
- dir: string;
59
- /** maxCount of record before file is uploaded to destination */
60
- maxCount: number;
61
- /** max time (in ms) before file is uploaded to destination (which ever comes first with maxCount) */
62
- maxTime: number;
63
-
64
- /* Mehod to process the file (.e.g., upload to bucket, bigquery, ...) */
65
- fileProcessor?: FileProcessor;
66
-
67
- /* Optional recordSerializer to file. By default, JSON.serializer() (new line json) */
68
- recordSerializer?: RecordSerializer<R>;
69
- }
70
-
71
-
72
-
73
- export class FileLogWriter<R> implements LogWriter<R> {
74
-
75
- readonly name: string;
76
- private dir: string;
77
- private maxCount: number;
78
- private maxTime: number;
79
- private fileProcessor?: FileProcessor;
80
- private recordSerializer: RecordSerializer<R>;
81
-
82
- private _init = false;
83
- private _rev = 0;
84
- private count = 0;
85
- private nextUpload: number | null = null; // null means nothing scheduled
86
- private lastUpload?: number;
87
-
88
- private file?: string;
89
-
90
-
91
- constructor(opts: FileLogWriterOptions<R>) {
92
- this.name = opts.name;
93
- this.maxCount = opts.maxCount;
94
- this.maxTime = opts.maxTime;
95
- this.dir = opts.dir;
96
- this.fileProcessor = opts.fileProcessor;
97
- this.recordSerializer = opts.recordSerializer ?? defaultSerializer;
98
- }
99
-
100
- private async init() {
101
- if (!this._init) {
102
- await fs.mkdirs(this.dir);
103
-
104
- // delete the logs dir if exit
105
- const oldLogFiles = await glob(this.dir + `${this.name}*.log`);
106
- await saferRemove(oldLogFiles);
107
- console.log('Deleted old log files', oldLogFiles);
108
-
109
- this.rev();
110
-
111
- this._init = true;
112
- }
113
-
114
- }
115
-
116
- /** Update the revision file */
117
- private rev() {
118
- this.count = 0;
119
- this._rev = this._rev + 1;
120
- const suffix = `${this._rev}`.padStart(5, '0');
121
- this.file = Path.join(this.dir, `${this.name}-${suffix}.log`)
122
- }
123
-
124
-
125
- /** IMPLEMENTATION of the FileWriter interface */
126
- async writeRec(rec: R) {
127
-
128
- if (!this._init) {
129
- await this.init();
130
- }
131
-
132
- // TODO: Need to move this outside of the generic log implementation (we do this because bigquery expect info to be string, since it can be dynamic)
133
- // NOTE: In fact, this whole file write and upload, should be part of a FileLogWriter, and we just treat it as above (perhaps in the BigQueryLogWriter extends FileLogWriter)
134
- const str = this.recordSerializer(rec);
135
- if (str != null) {
136
- await fs.appendFile(this.file!, str);
137
- }
138
-
139
- // add count
140
- this.count = this.count + 1;
141
-
142
- // if we are above the count, we upload
143
- if (this.count > this.maxCount) {
144
- await this.endFile();
145
- }
146
- // if still below the count, but do not have this.nextUpload, schedule one
147
- else if (this.nextUpload === null) {
148
- const maxTimeMs = this.maxTime * 1000; // in ms
149
-
150
- const nextUpload = Date.now() + maxTimeMs;
151
- this.nextUpload = nextUpload;
152
-
153
- setTimeout(async () => {
154
- // perform only if this.nextUpload match the scheduled nextUpload (otherwise, was already processed and this schedule is outdated)
155
- if (this.nextUpload === nextUpload) {
156
- await this.endFile();
157
- }
158
- }, maxTimeMs);
159
- }
160
-
161
- }
162
-
163
- private async endFile() {
164
- const file = this.file!;
165
- // we rev just before to make sure other logs will happen on new files
166
- this.rev();
167
-
168
- try {
169
- const exists = await fs.pathExists(file);
170
- if (exists) {
171
- if (this.fileProcessor) {
172
- await this.fileProcessor(file);
173
- }
174
- await saferRemove(file);
175
- } else {
176
- console.log(`CODE ERROR - can't upload to big query ${file} does not exists`);
177
- }
178
-
179
- } catch (ex) {
180
- console.log(`ERROR - logger.processLogFile - cannot upload to big query ${file}, ${ex.message}`);
181
- await fs.rename(file, file + '.error');
182
- }
183
-
184
- this.count = 0;
185
- this.lastUpload = Date.now();
186
- this.nextUpload = null;
187
- }
188
-
189
- }
190
-
191
- /** default serializer */
192
-
193
- function defaultSerializer<R>(rec: R): string {
194
- return isString(rec) ? rec : JSON.stringify(rec);
195
- }
196
-
197
- //#endregion ---------- /FileLogWriter ----------
198
-
199
-
package/dist/fs.d.ts DELETED
@@ -1,12 +0,0 @@
1
- import { Options } from 'fast-glob';
2
- /**
3
- * Simplified and sorted glob function (using fast-glob) for one or more pattern from current directory or a optional cwd one.
4
- *
5
- * Note 1: The result will be sorted by natural directory/subdir/filename order (as a would a recursive walk)
6
- * Note 2: When `cwd` in options, it is added to the file path i.e. `pathJoin(cwd, path)`
7
- *
8
- * @returns always sorted result return Promise<string[]>
9
- */
10
- export declare function glob(pattern: string | string[], cwdOrFastGlobOptions?: string | Options): Promise<string[]>;
11
- /** Remove one or more files. Resolved the number of names removed */
12
- export declare function saferRemove(names: string | string[], cwd?: string): Promise<string[]>;
package/dist/fs.js DELETED
@@ -1,77 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.saferRemove = exports.glob = void 0;
7
- const fast_glob_1 = __importDefault(require("fast-glob"));
8
- const fs_extra_1 = require("fs-extra");
9
- const path_1 = require("path");
10
- const utils_min_1 = require("utils-min");
11
- /**
12
- * Simplified and sorted glob function (using fast-glob) for one or more pattern from current directory or a optional cwd one.
13
- *
14
- * Note 1: The result will be sorted by natural directory/subdir/filename order (as a would a recursive walk)
15
- * Note 2: When `cwd` in options, it is added to the file path i.e. `pathJoin(cwd, path)`
16
- *
17
- * @returns always sorted result return Promise<string[]>
18
- */
19
- async function glob(pattern, cwdOrFastGlobOptions) {
20
- let opts = undefined;
21
- if (cwdOrFastGlobOptions != null) {
22
- opts = (typeof cwdOrFastGlobOptions === 'string') ? { cwd: cwdOrFastGlobOptions } : cwdOrFastGlobOptions;
23
- }
24
- const result = await fast_glob_1.default(pattern, opts);
25
- const cwd = (opts) ? opts.cwd : undefined;
26
- const list = result.map(path => {
27
- return (cwd) ? path_1.join(cwd, path) : path;
28
- });
29
- return list.sort(globCompare);
30
- }
31
- exports.glob = glob;
32
- /** Remove one or more files. Resolved the number of names removed */
33
- async function saferRemove(names, cwd) {
34
- const baseDir = (cwd) ? path_1.resolve(cwd) : path_1.resolve('./');
35
- let removedNames = [];
36
- for (const name of utils_min_1.asArray(names)) {
37
- const fullPath = path_1.join(baseDir, name);
38
- if (!fullPath.startsWith(baseDir)) {
39
- throw new Error(`Path to be removed does not look safe (nothing done): ${fullPath}\n\tCause: Does not belong to ${baseDir}`);
40
- }
41
- const exists = await fs_extra_1.pathExists(fullPath);
42
- if (exists) {
43
- await fs_extra_1.remove(fullPath);
44
- removedNames.push(name);
45
- }
46
- }
47
- return removedNames;
48
- }
49
- exports.saferRemove = saferRemove;
50
- //#region ---------- Utils ----------
51
- function globCompare(a, b) {
52
- const aPathIdxs = pathIndexes(a);
53
- const bPathIdxs = pathIndexes(b);
54
- const minIdx = Math.min(aPathIdxs.length, bPathIdxs.length) - 1;
55
- const aMinPath = a.substring(0, aPathIdxs[minIdx]);
56
- const bMinPath = b.substring(0, bPathIdxs[minIdx]);
57
- // if the common path is the same, and the path depth is different, then, the shortest one come first;
58
- if ((aMinPath === bMinPath) && (aPathIdxs.length !== bPathIdxs.length)) {
59
- return (aPathIdxs.length < bPathIdxs.length) ? -1 : 1;
60
- }
61
- // otherwise, we do a normal compare
62
- return (a < b) ? -1 : 1;
63
- }
64
- function pathIndexes(fullPath) {
65
- const idxs = [];
66
- const l = fullPath.length;
67
- for (let i = 0; i < l; i++) {
68
- if (fullPath[i] === '/') {
69
- idxs.push(i);
70
- }
71
- }
72
- return idxs;
73
- }
74
- // function asArray(names: string | string[]) {
75
- // return (names instanceof Array) ? names : [names];
76
- // }
77
- //#endregion ---------- /Utils ----------
package/src/fs.ts DELETED
@@ -1,84 +0,0 @@
1
- import FastGlob, { Options } from 'fast-glob';
2
- import { pathExists, remove } from 'fs-extra';
3
- import { join as pathJoin, resolve as pathResolve } from 'path';
4
- import { asArray } from 'utils-min';
5
-
6
- /**
7
- * Simplified and sorted glob function (using fast-glob) for one or more pattern from current directory or a optional cwd one.
8
- *
9
- * Note 1: The result will be sorted by natural directory/subdir/filename order (as a would a recursive walk)
10
- * Note 2: When `cwd` in options, it is added to the file path i.e. `pathJoin(cwd, path)`
11
- *
12
- * @returns always sorted result return Promise<string[]>
13
- */
14
- export async function glob(pattern: string | string[], cwdOrFastGlobOptions?: string | Options): Promise<string[]> {
15
- let opts: Options | undefined = undefined;
16
-
17
- if (cwdOrFastGlobOptions != null) {
18
- opts = (typeof cwdOrFastGlobOptions === 'string') ? { cwd: cwdOrFastGlobOptions } : cwdOrFastGlobOptions;
19
- }
20
-
21
- const result = await FastGlob(pattern, opts);
22
- const cwd = (opts) ? opts.cwd : undefined;
23
- const list = result.map(path => {
24
- return (cwd) ? pathJoin(cwd, path) : path;
25
- });
26
- return list.sort(globCompare);
27
- }
28
-
29
- /** Remove one or more files. Resolved the number of names removed */
30
- export async function saferRemove(names: string | string[], cwd?: string): Promise<string[]> {
31
- const baseDir = (cwd) ? pathResolve(cwd) : pathResolve('./');
32
- let removedNames: string[] = [];
33
-
34
- for (const name of asArray(names)) {
35
- const fullPath = pathJoin(baseDir, name);
36
- if (!fullPath.startsWith(baseDir)) {
37
- throw new Error(`Path to be removed does not look safe (nothing done): ${fullPath}\n\tCause: Does not belong to ${baseDir}`);
38
- }
39
- const exists = await pathExists(fullPath);
40
- if (exists) {
41
- await remove(fullPath);
42
- removedNames.push(name);
43
- }
44
- }
45
- return removedNames;
46
- }
47
-
48
-
49
- //#region ---------- Utils ----------
50
- function globCompare(a: string, b: string) {
51
- const aPathIdxs = pathIndexes(a);
52
- const bPathIdxs = pathIndexes(b);
53
- const minIdx = Math.min(aPathIdxs.length, bPathIdxs.length) - 1;
54
- const aMinPath = a.substring(0, aPathIdxs[minIdx]);
55
- const bMinPath = b.substring(0, bPathIdxs[minIdx]);
56
-
57
- // if the common path is the same, and the path depth is different, then, the shortest one come first;
58
- if ((aMinPath === bMinPath) && (aPathIdxs.length !== bPathIdxs.length)) {
59
- return (aPathIdxs.length < bPathIdxs.length) ? -1 : 1;
60
- }
61
-
62
- // otherwise, we do a normal compare
63
- return (a < b) ? -1 : 1;
64
-
65
-
66
- }
67
-
68
- function pathIndexes(fullPath: string): number[] {
69
- const idxs: number[] = [];
70
-
71
- const l = fullPath.length;
72
- for (let i = 0; i < l; i++) {
73
- if (fullPath[i] === '/') {
74
- idxs.push(i);
75
- }
76
- }
77
-
78
- return idxs;
79
- }
80
-
81
- // function asArray(names: string | string[]) {
82
- // return (names instanceof Array) ? names : [names];
83
- // }
84
- //#endregion ---------- /Utils ----------