jopi-toolkit 3.0.5 → 3.0.10
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 +1 -1
- package/dist/jk_crypto/jBundler_ifServer.d.ts +2 -2
- package/dist/jk_crypto/jBundler_ifServer.js +8 -8
- package/dist/jk_crypto/jBundler_ifServer.js.map +1 -1
- package/dist/jk_events/index.d.ts +5 -5
- package/dist/jk_events/index.js +20 -10
- package/dist/jk_events/index.js.map +1 -1
- package/dist/jk_fs/jBundler_ifBrowser.d.ts +2 -2
- package/dist/jk_fs/jBundler_ifBrowser.js +2 -2
- package/dist/jk_fs/jBundler_ifServer.d.ts +39 -3
- package/dist/jk_fs/jBundler_ifServer.js +152 -3
- package/dist/jk_fs/jBundler_ifServer.js.map +1 -1
- package/dist/jk_logs/index.d.ts +54 -0
- package/dist/jk_logs/index.js +316 -0
- package/dist/jk_logs/index.js.map +1 -0
- package/dist/jk_logs/jBundler_ifBrowser.js.map +1 -0
- package/dist/jk_logs/jBundler_ifServer.d.ts +2 -0
- package/dist/jk_logs/jBundler_ifServer.js +25 -0
- package/dist/jk_logs/jBundler_ifServer.js.map +1 -0
- package/dist/jk_schemas/index.js +2 -0
- package/dist/jk_schemas/index.js.map +1 -1
- package/dist/jk_timer/index.js +9 -4
- package/dist/jk_timer/index.js.map +1 -1
- package/dist/jk_tools/common.d.ts +29 -0
- package/dist/jk_tools/common.js +89 -0
- package/dist/jk_tools/common.js.map +1 -0
- package/dist/jk_tools/index.d.ts +1 -20
- package/dist/jk_tools/index.js +1 -71
- package/dist/jk_tools/index.js.map +1 -1
- package/dist/jk_tools/jBundler_ifBrowser.d.ts +1 -0
- package/dist/jk_tools/jBundler_ifBrowser.js +2 -0
- package/dist/jk_tools/jBundler_ifBrowser.js.map +1 -0
- package/dist/jk_tools/jBundler_ifServer.d.ts +22 -0
- package/dist/jk_tools/jBundler_ifServer.js +75 -0
- package/dist/jk_tools/jBundler_ifServer.js.map +1 -0
- package/package.json +11 -4
- package/src/jk_crypto/jBundler_ifServer.ts +8 -8
- package/src/jk_events/index.js +17 -7
- package/src/jk_events/index.ts +24 -3
- package/src/jk_fs/jBundler_ifBrowser.ts +2 -2
- package/src/jk_fs/jBundler_ifServer.js +552 -0
- package/src/jk_fs/jBundler_ifServer.ts +171 -4
- package/src/jk_logs/index.ts +444 -0
- package/src/jk_logs/jBundler_ifBrowser.ts +2 -0
- package/src/jk_logs/jBundler_ifServer.ts +27 -0
- package/src/jk_thread/common.js +7 -0
- package/src/jk_tools/common.js +101 -0
- package/src/jk_tools/common.ts +99 -0
- package/src/jk_tools/index.js +1 -82
- package/src/jk_tools/index.ts +1 -82
- package/src/jk_tools/jBundler_ifBrowser.ts +1 -0
- package/src/jk_tools/jBundler_ifServer.js +167 -0
- package/src/jk_tools/jBundler_ifServer.ts +109 -0
- package/src/jk_what/jBundler_ifServer.js +5 -0
- package/dist/jk_appResolver/common.d.ts +0 -40
- package/dist/jk_appResolver/common.js +0 -306
- package/dist/jk_appResolver/common.js.map +0 -1
- package/dist/jk_appResolver/index.d.ts +0 -30
- package/dist/jk_appResolver/index.js +0 -276
- package/dist/jk_appResolver/index.js.map +0 -1
- package/dist/jk_appResolver/jBundler_ifBrowser.js.map +0 -1
- package/dist/jk_appResolver/jBundler_ifServer.d.ts +0 -1
- package/dist/jk_appResolver/jBundler_ifServer.js +0 -9
- package/dist/jk_appResolver/jBundler_ifServer.js.map +0 -1
- package/dist/jk_linker/TypeComposite.d.ts +0 -12
- package/dist/jk_linker/TypeComposite.js +0 -112
- package/dist/jk_linker/TypeComposite.js.map +0 -1
- package/dist/jk_linker/arobaseType_List.d.ts +0 -44
- package/dist/jk_linker/arobaseType_List.js +0 -183
- package/dist/jk_linker/arobaseType_List.js.map +0 -1
- package/dist/jk_linker/arobaseTypes.d.ts +0 -45
- package/dist/jk_linker/arobaseTypes.js +0 -181
- package/dist/jk_linker/arobaseTypes.js.map +0 -1
- package/dist/jk_linker/binding.d.ts +0 -1
- package/dist/jk_linker/binding.js +0 -13
- package/dist/jk_linker/binding.js.map +0 -1
- package/dist/jk_linker/engine.d.ts +0 -119
- package/dist/jk_linker/engine.js +0 -553
- package/dist/jk_linker/engine.js.map +0 -1
- package/dist/jk_linker/index.d.ts +0 -1
- package/dist/jk_linker/index.js +0 -2
- package/dist/jk_linker/index.js.map +0 -1
- package/dist/jk_linker/install.d.ts +0 -4
- package/dist/jk_linker/install.js +0 -44
- package/dist/jk_linker/install.js.map +0 -1
- package/dist/jk_linker/jBundler_ifServer.d.ts +0 -3
- package/dist/jk_linker/jBundler_ifServer.js +0 -4
- package/dist/jk_linker/jBundler_ifServer.js.map +0 -1
- package/dist/jk_linker/modulesInitProcessor.d.ts +0 -7
- package/dist/jk_linker/modulesInitProcessor.js +0 -36
- package/dist/jk_linker/modulesInitProcessor.js.map +0 -1
- package/dist/jk_linker/typeChunks.d.ts +0 -15
- package/dist/jk_linker/typeChunks.js +0 -42
- package/dist/jk_linker/typeChunks.js.map +0 -1
- package/dist/jk_linker/typeComposites.d.ts +0 -13
- package/dist/jk_linker/typeComposites.js +0 -121
- package/dist/jk_linker/typeComposites.js.map +0 -1
- package/dist/jk_linker/typeEvents.d.ts +0 -5
- package/dist/jk_linker/typeEvents.js +0 -29
- package/dist/jk_linker/typeEvents.js.map +0 -1
- package/dist/jk_linker/typeList.d.ts +0 -30
- package/dist/jk_linker/typeList.js +0 -144
- package/dist/jk_linker/typeList.js.map +0 -1
- package/dist/jk_linker/typeListeners.d.ts +0 -21
- package/dist/jk_linker/typeListeners.js +0 -188
- package/dist/jk_linker/typeListeners.js.map +0 -1
- package/dist/jk_linker/typeReplaces.d.ts +0 -8
- package/dist/jk_linker/typeReplaces.js +0 -41
- package/dist/jk_linker/typeReplaces.js.map +0 -1
- package/dist/jk_registry/index.d.ts +0 -11
- package/dist/jk_registry/index.js +0 -36
- package/dist/jk_registry/index.js.map +0 -1
- /package/dist/{jk_appResolver → jk_logs}/jBundler_ifBrowser.d.ts +0 -0
- /package/dist/{jk_appResolver → jk_logs}/jBundler_ifBrowser.js +0 -0
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
// noinspection JSUnusedGlobalSymbols
|
|
2
2
|
|
|
3
3
|
import fs from "node:fs/promises";
|
|
4
|
-
import fss, {createReadStream} from "node:fs";
|
|
4
|
+
import fss, {createReadStream, createWriteStream} from "node:fs";
|
|
5
5
|
import {fileURLToPath as n_fileURLToPath, pathToFileURL as n_pathToFileURL } from "node:url";
|
|
6
6
|
import {lookup} from "mime-types";
|
|
7
7
|
import {Readable} from "node:stream";
|
|
8
8
|
import path from "node:path";
|
|
9
|
+
import {createHash} from "node:crypto";
|
|
9
10
|
import {isBunJS} from "jopi-toolkit/jk_what";
|
|
10
11
|
import type {DirItem, FileState} from "./common.ts";
|
|
12
|
+
import AdmZip from 'adm-zip';
|
|
11
13
|
|
|
12
14
|
class WebToNodeReadableStreamAdapter extends Readable {
|
|
13
15
|
private webStreamReader: ReadableStreamDefaultReader<any>;
|
|
@@ -170,7 +172,7 @@ export async function writeTextToFile(filePath: string, text: string, createDir:
|
|
|
170
172
|
await fs.writeFile(filePath, text, {encoding: 'utf8', flag: 'w'});
|
|
171
173
|
}
|
|
172
174
|
|
|
173
|
-
export function
|
|
175
|
+
export function writeTextToFileSync(filePath: string, text: string, createDir: boolean = true): void {
|
|
174
176
|
if (createDir) {
|
|
175
177
|
try {
|
|
176
178
|
fss.mkdirSync(path.dirname(filePath), {recursive: true});
|
|
@@ -183,7 +185,12 @@ export function readTextFromFile(filePath: string): Promise<string> {
|
|
|
183
185
|
return fs.readFile(filePath, 'utf8');
|
|
184
186
|
}
|
|
185
187
|
|
|
186
|
-
export function
|
|
188
|
+
export async function readJsonFromFile<T = any>(filePath: string): Promise<T> {
|
|
189
|
+
let txt = await fs.readFile(filePath, 'utf8');
|
|
190
|
+
return JSON.parse(txt) as T;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function readTextFromFileSync(filePath: string): string {
|
|
187
194
|
return fss.readFileSync(filePath, 'utf8');
|
|
188
195
|
}
|
|
189
196
|
|
|
@@ -287,6 +294,56 @@ export async function listDir(dirPath: string): Promise<DirItem[]> {
|
|
|
287
294
|
return result;
|
|
288
295
|
}
|
|
289
296
|
|
|
297
|
+
const MEGA = 1024 * 1024;
|
|
298
|
+
|
|
299
|
+
async function calcFileHash_bun(filePath: string): Promise<string|undefined> {
|
|
300
|
+
const file = Bun.file(filePath);
|
|
301
|
+
if (!await file.exists()) return undefined;
|
|
302
|
+
|
|
303
|
+
if (file.size>10 * MEGA) {
|
|
304
|
+
return calcFileHash_bun_streamed(file)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return Bun.hash(await file.arrayBuffer(), 12346).toString();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function calcFileHash_bun_streamed(file: Bun.BunFile): Promise<string|undefined> {
|
|
311
|
+
const stream = file.stream();
|
|
312
|
+
const reader = stream.getReader();
|
|
313
|
+
const hasher = new Bun.CryptoHasher("sha256");
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
while (true) {
|
|
317
|
+
const { done, value } = await reader.read();
|
|
318
|
+
if (done) break;
|
|
319
|
+
hasher.update(value);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return hasher.digest("hex");
|
|
323
|
+
} finally {
|
|
324
|
+
reader.releaseLock();
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function calcFileHash_node(filePath: string): Promise<string|undefined> {
|
|
329
|
+
if (!isFile(filePath)) return Promise.resolve(undefined);
|
|
330
|
+
|
|
331
|
+
return new Promise((resolve, reject) => {
|
|
332
|
+
const hash = createHash('sha256');
|
|
333
|
+
const stream = createReadStream(filePath);
|
|
334
|
+
stream.on('data', (data) => hash.update(data));
|
|
335
|
+
stream.on('end', () => resolve(hash.digest('hex')));
|
|
336
|
+
stream.on('error', (error) => reject(error));
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Calculate the hash of a file.
|
|
342
|
+
* Allows using it for HTTP ETag or another change proof.
|
|
343
|
+
* This version is optimized to use streams and avoid loading the whole file in memory.
|
|
344
|
+
*/
|
|
345
|
+
export const calcFileHash = isBunJS ? calcFileHash_bun : calcFileHash_node;
|
|
346
|
+
|
|
290
347
|
/**
|
|
291
348
|
* Convert a simple win32 path to a linux path.
|
|
292
349
|
*/
|
|
@@ -294,6 +351,114 @@ export function win32ToLinuxPath(filePath: string): string {
|
|
|
294
351
|
return filePath.replace(/\\/g, '/');
|
|
295
352
|
}
|
|
296
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Copy a directory recursively.
|
|
356
|
+
* Is optimized for large files.
|
|
357
|
+
*/
|
|
358
|
+
export async function copyDirectory(srcDir: string, destDir: string): Promise<void> {
|
|
359
|
+
if (!await isDirectory(srcDir)) {
|
|
360
|
+
throw new Error(`Directory doesn't exist : ${srcDir}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
await mkDir(destDir);
|
|
364
|
+
const entries = await fs.readdir(srcDir, { withFileTypes: true });
|
|
365
|
+
|
|
366
|
+
await Promise.all(entries.map(async (entry) => {
|
|
367
|
+
const srcPath = path.join(srcDir, entry.name);
|
|
368
|
+
const destPath = path.join(destDir, entry.name);
|
|
369
|
+
|
|
370
|
+
if (entry.isDirectory()) {
|
|
371
|
+
await copyDirectory(srcPath, destPath);
|
|
372
|
+
} else {
|
|
373
|
+
try {
|
|
374
|
+
await copyFile(srcPath, destPath);
|
|
375
|
+
} catch {
|
|
376
|
+
console.warn(`jk_fs.copyDirectory - Failed to copy file ${srcPath}`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Copy of a file.
|
|
384
|
+
* Is optimized for large files.
|
|
385
|
+
*/
|
|
386
|
+
export async function copyFile(srcPath: string, destPath: string): Promise<void> {
|
|
387
|
+
let stat = await getFileStat(srcPath);
|
|
388
|
+
if (!stat) return;
|
|
389
|
+
|
|
390
|
+
// Assert the symlink exist.
|
|
391
|
+
if (stat.isSymbolicLink()) {
|
|
392
|
+
const symStat = await fs.lstat(srcPath);
|
|
393
|
+
if (!symStat) return;
|
|
394
|
+
if (!symStat.isFile()) return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return new Promise<void>((resolve, reject) => {
|
|
398
|
+
const readStream = createReadStream(srcPath);
|
|
399
|
+
const writeStream = createWriteStream(destPath);
|
|
400
|
+
|
|
401
|
+
readStream.on('error', reject);
|
|
402
|
+
writeStream.on('error', reject);
|
|
403
|
+
writeStream.on('finish', resolve);
|
|
404
|
+
|
|
405
|
+
readStream.pipe(writeStream);
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Unzip a .zip file in an optimized way.
|
|
411
|
+
* Note was using : "@types/unzipper": "^0.10.11"
|
|
412
|
+
* which has some bug, files/folders was forgottens.
|
|
413
|
+
* import * as unzipper from "unzipper";
|
|
414
|
+
*/
|
|
415
|
+
/*export async function unzipFile_old(zipFilePath: string, outputDir: string): Promise<void> {
|
|
416
|
+
if (!await isFile(zipFilePath)) {
|
|
417
|
+
throw new Error(`File doesn't exist : ${zipFilePath}`);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (!await isDirectory(outputDir)) {
|
|
421
|
+
await mkDir(outputDir);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
await createReadStream(zipFilePath)
|
|
425
|
+
.pipe(unzipper.Extract({path: outputDir}))
|
|
426
|
+
.promise();
|
|
427
|
+
}*/
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Unzip a .zip file in an optimized way.
|
|
431
|
+
*/
|
|
432
|
+
export async function unzipFile(zipFilePath: string, outputDir: string): Promise<void> {
|
|
433
|
+
if (!await isFile(zipFilePath)) {
|
|
434
|
+
throw new Error(`File doesn't exist : ${zipFilePath}`);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (!await isDirectory(outputDir)) {
|
|
438
|
+
await mkDir(outputDir);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const zip = new AdmZip(zipFilePath);
|
|
442
|
+
zip.extractAllTo(outputDir, true);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Create a temporary directory.
|
|
447
|
+
* Return an object containing the directory path and a cleanup function.
|
|
448
|
+
*/
|
|
449
|
+
export async function createTempDir(prefix: string): Promise<{path: string, remove: ()=>Promise<void>}> {
|
|
450
|
+
const dirPath = await fs.mkdtemp(prefix);
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
path: dirPath,
|
|
454
|
+
remove: async () => {
|
|
455
|
+
return fs.rm(dirPath, {recursive: true, force: true});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
//region Node.js functions
|
|
461
|
+
|
|
297
462
|
export const join = path.join;
|
|
298
463
|
export const resolve = path.resolve;
|
|
299
464
|
export const dirname = path.dirname;
|
|
@@ -305,4 +470,6 @@ export const normalize = path.normalize;
|
|
|
305
470
|
export const basename = path.basename;
|
|
306
471
|
|
|
307
472
|
export const symlink = fs.symlink;
|
|
308
|
-
export const rename = fs.rename;
|
|
473
|
+
export const rename = fs.rename;
|
|
474
|
+
|
|
475
|
+
//endregion
|
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
// noinspection JSUnusedGlobalSymbols
|
|
2
|
+
|
|
3
|
+
import * as jk_terms from "jopi-toolkit/jk_term";
|
|
4
|
+
import {init} from "./jBundler_ifServer.ts";
|
|
5
|
+
|
|
6
|
+
//region Common
|
|
7
|
+
|
|
8
|
+
export interface LogEntry {
|
|
9
|
+
level: LogLevel;
|
|
10
|
+
logger: string;
|
|
11
|
+
|
|
12
|
+
date: number;
|
|
13
|
+
title?: string;
|
|
14
|
+
data?: any;
|
|
15
|
+
|
|
16
|
+
timeDif?: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type LogEntryFormater = (entry: LogEntry) => string;
|
|
20
|
+
|
|
21
|
+
export enum LogLevel {
|
|
22
|
+
SPAM = 1,
|
|
23
|
+
INFO = 2,
|
|
24
|
+
WARN = 3,
|
|
25
|
+
ERROR = 4,
|
|
26
|
+
NONE = 10
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type LogCall = string | ((w: LogLevelHandler)=>void);
|
|
30
|
+
|
|
31
|
+
//endregion
|
|
32
|
+
|
|
33
|
+
//region Formater
|
|
34
|
+
|
|
35
|
+
const RED = jk_terms.C_RED;
|
|
36
|
+
const ORANGE = jk_terms.C_ORANGE;
|
|
37
|
+
const GREY = jk_terms.C_GREY;
|
|
38
|
+
const LIGHT_BLUE = jk_terms.C_LIGHT_BLUE;
|
|
39
|
+
const RESET = jk_terms.T_RESET;
|
|
40
|
+
|
|
41
|
+
export function formatDate1(timeStamp: number): string {
|
|
42
|
+
const date = new Date(timeStamp);
|
|
43
|
+
return date.toISOString();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const formater_simpleJson: LogEntryFormater = (entry: LogEntry) => {
|
|
47
|
+
return JSON.stringify(entry);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const formater_dateTypeTitleSourceData: LogEntryFormater = (entry: LogEntry) => {
|
|
51
|
+
const date = formatDate1(entry.date);
|
|
52
|
+
|
|
53
|
+
let json = entry.data ? JSON.stringify(entry.data) : "";
|
|
54
|
+
const title = (entry.title || "").padEnd(50, " ");
|
|
55
|
+
|
|
56
|
+
json = entry.logger + " |>" + json;
|
|
57
|
+
|
|
58
|
+
switch (entry.level) {
|
|
59
|
+
case LogLevel.ERROR:
|
|
60
|
+
return `${date} - ERROR - ${title}${json}`;
|
|
61
|
+
case LogLevel.WARN:
|
|
62
|
+
return `${date} - WARN - ${title}${json}`;
|
|
63
|
+
case LogLevel.INFO:
|
|
64
|
+
return `${date} - INFO - ${title}${json}`;
|
|
65
|
+
case LogLevel.SPAM:
|
|
66
|
+
return `${date} - SPAM - ${title}${json}`;
|
|
67
|
+
default:
|
|
68
|
+
return "";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export const formater_typeTitleSourceData_colored: LogEntryFormater = (entry: LogEntry) => {
|
|
73
|
+
let json = entry.data ? JSON.stringify(entry.data) : "";
|
|
74
|
+
const title = (entry.title || "").padEnd(50, " ");
|
|
75
|
+
|
|
76
|
+
json = entry.timeDif === undefined
|
|
77
|
+
? `${entry.logger} ${json}` : `${entry.logger} (${entry.timeDif} ms) ${json}`;
|
|
78
|
+
|
|
79
|
+
switch (entry.level) {
|
|
80
|
+
case LogLevel.ERROR:
|
|
81
|
+
return `${RED}error${RESET} - ${title}${GREY}${json}${RESET}`;
|
|
82
|
+
case LogLevel.WARN:
|
|
83
|
+
return `${ORANGE}warn ${RESET} - ${title}${GREY}${json}${RESET}`;
|
|
84
|
+
case LogLevel.INFO:
|
|
85
|
+
return `${LIGHT_BLUE}info ${RESET} - ${title}${GREY}${json}${RESET}`;
|
|
86
|
+
case LogLevel.SPAM:
|
|
87
|
+
return `${GREY}spam ${RESET} - ${title}${GREY}${json}${RESET}`;
|
|
88
|
+
default:
|
|
89
|
+
return "";
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
//endregion
|
|
94
|
+
|
|
95
|
+
//region LogWriter
|
|
96
|
+
|
|
97
|
+
export interface LogWriter {
|
|
98
|
+
addEntry(entry: LogEntry): void;
|
|
99
|
+
addBatch(entries: LogEntry[]): void;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
class ConsoleLogWriter implements LogWriter {
|
|
103
|
+
constructor(private readonly formater: LogEntryFormater = gDefaultFormater) {
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
addEntry(entry: LogEntry): void {
|
|
107
|
+
console.log(this.formater(entry));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
addBatch(entries: LogEntry[]) {
|
|
111
|
+
entries.forEach(e => this.addEntry(e));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export class VoidLogWriter implements LogWriter {
|
|
116
|
+
addBatch(_entries: LogEntry[]): void {
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
addEntry(_entry: LogEntry): void {
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function setDefaultWriter(writer: LogWriter) {
|
|
124
|
+
gDefaultWriter = writer;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function getDefaultWriter(): LogWriter {
|
|
128
|
+
return gDefaultWriter;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function setDefaultFormater(formater: LogEntryFormater) {
|
|
132
|
+
gDefaultFormater = formater;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function getDefaultFormater(): LogEntryFormater {
|
|
136
|
+
return gDefaultFormater;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let gDefaultFormater: LogEntryFormater = formater_typeTitleSourceData_colored;
|
|
140
|
+
let gDefaultWriter: LogWriter = new ConsoleLogWriter();
|
|
141
|
+
|
|
142
|
+
//endregion
|
|
143
|
+
|
|
144
|
+
//region JopiLogger
|
|
145
|
+
|
|
146
|
+
export interface Logger {
|
|
147
|
+
get fullName(): string;
|
|
148
|
+
|
|
149
|
+
spam(_l?: LogCall): boolean;
|
|
150
|
+
info(_l?: LogCall): boolean;
|
|
151
|
+
warn(_l?: LogCall): boolean;
|
|
152
|
+
error(_l?: LogCall): boolean
|
|
153
|
+
|
|
154
|
+
beginSpam(l: LogCall): LoggerGroupCallback;
|
|
155
|
+
beginInfo(l: LogCall): LoggerGroupCallback;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function getLogger(name: string, parent?: Logger): Logger {
|
|
159
|
+
let fullName = name;
|
|
160
|
+
if (parent) fullName = parent.fullName + '.' + name;
|
|
161
|
+
|
|
162
|
+
let level = getLogLevelFor(fullName);
|
|
163
|
+
|
|
164
|
+
switch (level) {
|
|
165
|
+
case LogLevel.SPAM:
|
|
166
|
+
return new Logger_Spam(parent as JopiLogger, name);
|
|
167
|
+
case LogLevel.INFO:
|
|
168
|
+
return new Logger_Info(parent as JopiLogger, name);
|
|
169
|
+
case LogLevel.WARN:
|
|
170
|
+
return new Logger_Warn(parent as JopiLogger, name);
|
|
171
|
+
case LogLevel.ERROR:
|
|
172
|
+
return new Logger_Error(parent as JopiLogger, name);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return new Logger_None(parent as JopiLogger, name);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
abstract class JopiLogger implements Logger {
|
|
179
|
+
public readonly fullName: string;
|
|
180
|
+
private _onLog: LogWriter = gDefaultWriter;
|
|
181
|
+
|
|
182
|
+
protected readonly hSpam: LogLevelHandler;
|
|
183
|
+
protected readonly hInfo: LogLevelHandler;
|
|
184
|
+
protected readonly hWarn: LogLevelHandler;
|
|
185
|
+
protected readonly hError: LogLevelHandler;
|
|
186
|
+
|
|
187
|
+
private timeDif?: number;
|
|
188
|
+
private extraData?: any;
|
|
189
|
+
|
|
190
|
+
constructor(parent: JopiLogger|null, public readonly name: string) {
|
|
191
|
+
this.fullName = parent ? parent.fullName + '.' + name : name;
|
|
192
|
+
|
|
193
|
+
if (parent) {
|
|
194
|
+
this._onLog = parent._onLog;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const me = this;
|
|
198
|
+
|
|
199
|
+
this.hSpam = (title?: string, data?: any) => {
|
|
200
|
+
let td = this.timeDif;
|
|
201
|
+
this.timeDif = undefined;
|
|
202
|
+
data = this.mergeData(data);
|
|
203
|
+
|
|
204
|
+
me._onLog.addEntry({
|
|
205
|
+
level: LogLevel.SPAM,
|
|
206
|
+
logger: me.fullName, date: Date.now(), title, data, timeDif: td });
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
this.hInfo = (title?: string, data?: any) => {
|
|
210
|
+
let td = this.timeDif;
|
|
211
|
+
this.timeDif = undefined;
|
|
212
|
+
data = this.mergeData(data);
|
|
213
|
+
|
|
214
|
+
me._onLog.addEntry({
|
|
215
|
+
level: LogLevel.INFO,
|
|
216
|
+
logger: me.fullName, date: Date.now(), title, data, timeDif: td });
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
this.hWarn = (title?: string, data?: any) => {
|
|
220
|
+
let td = this.timeDif;
|
|
221
|
+
this.timeDif = undefined;
|
|
222
|
+
data = this.mergeData(data);
|
|
223
|
+
|
|
224
|
+
me._onLog.addEntry({
|
|
225
|
+
level: LogLevel.WARN,
|
|
226
|
+
logger: me.fullName, date: Date.now(), title, data, timeDif: td });
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
this.hError = (title?: string, data?: any) => {
|
|
230
|
+
let td = this.timeDif;
|
|
231
|
+
this.timeDif = undefined;
|
|
232
|
+
data = this.mergeData(data);
|
|
233
|
+
|
|
234
|
+
me._onLog.addEntry({
|
|
235
|
+
level: LogLevel.ERROR,
|
|
236
|
+
logger: me.fullName, date: Date.now(), title, data, timeDif: td });
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private mergeData(data?: any): any|undefined {
|
|
241
|
+
if (!this.extraData) return data;
|
|
242
|
+
const extraData = this.extraData;
|
|
243
|
+
this.extraData = undefined;
|
|
244
|
+
|
|
245
|
+
if (!data) return extraData;
|
|
246
|
+
for (let p in extraData) data[p] = extraData[p];
|
|
247
|
+
|
|
248
|
+
return data;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
setLogWriter(callback: LogWriter) {
|
|
252
|
+
if (!callback) callback = gDefaultWriter;
|
|
253
|
+
this._onLog = callback;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
spam(_l?: (w: LogLevelHandler)=>void): boolean {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
info(_l?: (w: LogLevelHandler)=>void): boolean {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
warn(_l?: (w: LogLevelHandler)=>void) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
error(_l?: (w: LogLevelHandler)=>void) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
beginSpam(l: (w: LogLevelHandler)=>void): LoggerGroupCallback {
|
|
273
|
+
return gVoidLoggerGroupCallback;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
beginInfo(l: (w: LogLevelHandler)=>void): LoggerGroupCallback {
|
|
277
|
+
return gVoidLoggerGroupCallback;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
protected doBegin(l: LogCall, w: LogLevelHandler): LoggerGroupCallback {
|
|
281
|
+
const startTime = Date.now();
|
|
282
|
+
|
|
283
|
+
return (data?: any) => {
|
|
284
|
+
this.timeDif = Date.now() - startTime;
|
|
285
|
+
this.doCall(l, w, data);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
protected doCall(l: LogCall|undefined, w: LogLevelHandler, data?: any) {
|
|
290
|
+
this.extraData = data;
|
|
291
|
+
|
|
292
|
+
if (l) {
|
|
293
|
+
if (l instanceof Function) {
|
|
294
|
+
l(w);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
w(l as string);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
class Logger_None extends JopiLogger {
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
class Logger_Spam extends JopiLogger {
|
|
309
|
+
override spam(l?: LogCall) {
|
|
310
|
+
return this.doCall(l, this.hSpam);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
override info(l?: LogCall) {
|
|
314
|
+
return this.doCall(l, this.hInfo);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
override warn(l?: LogCall) {
|
|
318
|
+
return this.doCall(l, this.hWarn);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
override error(l?: LogCall) {
|
|
322
|
+
return this.doCall(l, this.hError);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
override beginSpam(l: LogCall) {
|
|
326
|
+
return this.doBegin(l, this.hSpam);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
override beginInfo(l: LogCall): LoggerGroupCallback {
|
|
330
|
+
return this.doBegin(l, this.hInfo);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
class Logger_Info extends JopiLogger {
|
|
335
|
+
override info(l?: LogCall) {
|
|
336
|
+
return this.doCall(l, this.hInfo);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
override warn(l?: LogCall) {
|
|
340
|
+
return this.doCall(l, this.hWarn);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
override error(l?: LogCall) {
|
|
344
|
+
return this.doCall(l, this.hError);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
override beginInfo(l: LogCall): LoggerGroupCallback {
|
|
348
|
+
return this.doBegin(l, this.hInfo);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
class Logger_Warn extends JopiLogger {
|
|
353
|
+
override warn(l?: LogCall) {
|
|
354
|
+
return this.doCall(l, this.hWarn);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
override error(l?: LogCall) {
|
|
358
|
+
return this.doCall(l, this.hError);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
class Logger_Error extends JopiLogger {
|
|
363
|
+
override error(l?: LogCall) {
|
|
364
|
+
return this.doCall(l, this.hError);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export type LoggerGroupCallback = (data?: any) => void;
|
|
369
|
+
const gVoidLoggerGroupCallback = () => {};
|
|
370
|
+
|
|
371
|
+
//endregion
|
|
372
|
+
|
|
373
|
+
//region Log levels
|
|
374
|
+
|
|
375
|
+
type LogLevelHandler = (title?: string, data?: any|undefined)=>void;
|
|
376
|
+
|
|
377
|
+
function getLogLevelName(level: LogLevel) {
|
|
378
|
+
switch (level) {
|
|
379
|
+
case LogLevel.SPAM:
|
|
380
|
+
return "SPAM";
|
|
381
|
+
case LogLevel.ERROR:
|
|
382
|
+
return "ERROR";
|
|
383
|
+
case LogLevel.INFO:
|
|
384
|
+
return "INFO";
|
|
385
|
+
case LogLevel.WARN:
|
|
386
|
+
return "WARN";
|
|
387
|
+
case LogLevel.NONE:
|
|
388
|
+
return "NONE";
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function getLogLevelByName(name: string): LogLevel | undefined {
|
|
393
|
+
switch (name) {
|
|
394
|
+
case "NONE": return LogLevel.NONE;
|
|
395
|
+
case "SPAM": return LogLevel.SPAM;
|
|
396
|
+
case "INFO": return LogLevel.INFO;
|
|
397
|
+
case "WARN": return LogLevel.WARN;
|
|
398
|
+
case "ERROR": return LogLevel.ERROR;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return undefined;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
//endregion
|
|
405
|
+
|
|
406
|
+
//region Registry
|
|
407
|
+
|
|
408
|
+
export interface LogConfig {
|
|
409
|
+
level?: string;
|
|
410
|
+
writer?: string;
|
|
411
|
+
formater?: string;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export interface LogInitializer {
|
|
415
|
+
setLogLevel(name: string, config: LogConfig): void;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
class Initializer implements LogInitializer {
|
|
419
|
+
setLogLevel(name: string, config: LogConfig): void {
|
|
420
|
+
let logLevel = getLogLevelByName(config.level || "NONE");
|
|
421
|
+
if (!logLevel) logLevel = LogLevel.NONE;
|
|
422
|
+
gRegistry[name] = logLevel;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function getLogLevelFor(name: string) {
|
|
427
|
+
let entry = gRegistry[name];
|
|
428
|
+
if (entry!==undefined) return entry;
|
|
429
|
+
|
|
430
|
+
for (let prefix in gRegistry) {
|
|
431
|
+
if (name.startsWith(prefix + ".")) {
|
|
432
|
+
return gRegistry[prefix];
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return gDefaultLogLevel;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const gDefaultLogLevel: LogLevel = LogLevel.WARN;
|
|
440
|
+
const gRegistry: Record<string, LogLevel> = {};
|
|
441
|
+
|
|
442
|
+
//endregion
|
|
443
|
+
|
|
444
|
+
init(new Initializer());
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as jk_app from "jopi-toolkit/jk_app";
|
|
2
|
+
import * as jk_fs from "jopi-toolkit/jk_fs";
|
|
3
|
+
import type {LogInitializer} from "./index.ts";
|
|
4
|
+
|
|
5
|
+
export function init(init: LogInitializer) {
|
|
6
|
+
const mainDir = jk_app.findPackageJsonDir();
|
|
7
|
+
const filePath = jk_fs.join(mainDir, "logConfig.json");
|
|
8
|
+
|
|
9
|
+
if (!jk_fs.isFileSync(filePath)) return;
|
|
10
|
+
let text = jk_fs.readTextFromFileSync(jk_fs.join(mainDir, "logConfig.json"));
|
|
11
|
+
|
|
12
|
+
if (!text) return;
|
|
13
|
+
let logJson = JSON.parse(text)
|
|
14
|
+
if (!logJson.config) return;
|
|
15
|
+
|
|
16
|
+
for (let logName in logJson.config) {
|
|
17
|
+
let logConfig = logJson.config[logName];
|
|
18
|
+
|
|
19
|
+
if (typeof(logConfig)==="string") {
|
|
20
|
+
logConfig = {level: logConfig.toUpperCase().trim()};
|
|
21
|
+
} else if (!logConfig) {
|
|
22
|
+
logConfig = {level: "none" };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
init.setLogLevel(logName, logConfig);
|
|
26
|
+
}
|
|
27
|
+
}
|