@svayam-opensource/svm-util-log 1.0.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 ADDED
@@ -0,0 +1,82 @@
1
+ # @svayam-opensource/svm-util-log
2
+
3
+ Svayam's shared logging framework — the logger from `svm-util`, carrying only the
4
+ dependencies a logger needs.
5
+
6
+ **Mandated by POL-423.** All Svayam-owned Node code uses this framework and leaves messages
7
+ at the various levels, so a run can be reviewed afterwards for issue and problem resolution.
8
+
9
+ ## Why this package exists
10
+
11
+ `@svayam/svm-util` is one package: the logger, the helpers, database access and the API
12
+ server. Taking it whole installs **675 packages, 274 MB** — right for an API service, and the
13
+ whole cost for a command-line tool that wants one log line. `@svayam-opensource/gov` is
14
+ installed with `npm i -g` on adopters' laptops.
15
+
16
+ This package is **25 packages, 8.4 MB**.
17
+
18
+ It is also on the **public** scope, because `@svayam/svm-util` publishes to the internal
19
+ registry, which adopters of a public CLI cannot resolve. A logging mandate that makes the
20
+ governance CLI uninstallable is a mandate nothing can obey.
21
+
22
+ First package of the split — see [911#224](https://github.com/Svayamtech/911-SVM-LIB-SVC/issues/224).
23
+
24
+ ## Use
25
+
26
+ ```ts
27
+ import { createApplicationLogger, LogLevel } from "@svayam-opensource/svm-util-log";
28
+
29
+ const logger = createApplicationLogger({
30
+ appId: "gov-work",
31
+ logDir: "~/.gov/svm/logs",
32
+ methods: [
33
+ { METHOD: "console", LOGLEVEL: LogLevel.warn },
34
+ { METHOD: "file", LOGLEVEL: LogLevel.debug },
35
+ ],
36
+ });
37
+
38
+ logger.log(LogLevel.debug, "chose the api-key route", "gov-work:cli:sign-in-choice", "captureAgentKey");
39
+ ```
40
+
41
+ ### `pgm_cd` is the code path
42
+
43
+ `<package>:<dir>:<module>` — `gov-work:cli:sign-in-choice` is `src/cli/sign-in-choice.ts`.
44
+ The point is that a reader can get from a line back to the code without guessing (POL-426).
45
+
46
+ ### Turning it on in the field
47
+
48
+ Errors and warnings always come through. Everything below them is opt-in:
49
+
50
+ ```bash
51
+ NODE_DEBUG=gov-work:cli:sign-in-choice gov work # one module
52
+ NODE_DEBUG=gov-work:* gov work # the package
53
+ ```
54
+
55
+ That is POL-425: **the person who needs the log is usually not the person who can reproduce
56
+ the fault.** They turn it on, reproduce, and send the file.
57
+
58
+ ### Secrets
59
+
60
+ POL-427 is **C01**: no credential, key, token or PII reaches a log, at any level, in any
61
+ transport. `meta` is masked before rendering — by field name, case-insensitively, matching a
62
+ name that *contains* a masked word (`userPassword`, `db_pwd`, `authToken`). Extend
63
+ `DEFAULT_MASK`, never replace it.
64
+
65
+ ## Transports
66
+
67
+ `console` and `file` (daily-rotate) ship here. `database` and `api` belong to the packages
68
+ carrying `mysql` and `axios`, and register themselves:
69
+
70
+ ```ts
71
+ registerTransport("database", (conf, config) => new MyDbTransport(conf, config));
72
+ ```
73
+
74
+ A method declared in config with nothing registered for it is **ignored, not an error** — a
75
+ line that does not go somewhere, never a crash at startup.
76
+
77
+ ## Two deliberate departures from `svm-util`
78
+
79
+ 1. **Configuration is injected, never global.** `svm-util` reaches a module-level
80
+ `getSvayamConfiguration()`; a CLI has a first run where no configuration exists at all.
81
+ 2. **Console output goes to STDERR.** `stdout` is a program's answer, and a caller piping it
82
+ should not receive the log as data.
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_MASK = void 0;
4
+ /** What a `mask` covers before a consumer adds to it. Extend, do not replace. */
5
+ exports.DEFAULT_MASK = [
6
+ "password", "passwd", "pwd", "secret", "token", "apiKey", "api_key",
7
+ "authorization", "auth", "credential", "credentials", "privateKey", "private_key",
8
+ "sessionId", "session_id", "cookie", "jwt", "bearer",
9
+ ];
@@ -0,0 +1,114 @@
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.isLogEnabled = isLogEnabled;
7
+ exports.maskValues = maskValues;
8
+ exports.stringify = stringify;
9
+ exports.toLogEntry = toLogEntry;
10
+ exports.formatLogMessage = formatLogMessage;
11
+ /* -------------------------------------------------
12
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
13
+ * All rights reserved. This code may not be copied
14
+ * or duplicated in whole or part by any means
15
+ * without express prior agreement in writing.
16
+ * -----------------------------------------------*/
17
+ /**
18
+ * Turning a call into a line — masking, provenance, and the one-line layout.
19
+ *
20
+ * Extracted from `svm-util/src/svm-helper.ts` (`stringify`, `getLogEntry`, `formatLogMessage`,
21
+ * `isLogEnabled`), with the global configuration lookup removed: every function here takes
22
+ * what it needs. See `config.ts` for why.
23
+ */
24
+ const node_util_1 = __importDefault(require("node:util"));
25
+ const config_js_1 = require("./config.js");
26
+ const COMPACT = {
27
+ depth: 4, compact: true, breakLength: Infinity, maxStringLength: 512, colors: false,
28
+ };
29
+ /**
30
+ * Is logging switched on for this origin?
31
+ *
32
+ * `node:util.debuglog` is the switch, so `NODE_DEBUG=gov-work:cli:agent-install` enables one
33
+ * module and `NODE_DEBUG=gov-work:*` the package — POL-425's "switchable at run time by
34
+ * whoever is in front of the problem, with no rebuild and no release".
35
+ *
36
+ * NOT CACHED PER CALL BY ACCIDENT: `debuglog` itself is lazy and memoised, so asking it every
37
+ * time is cheap, and asking it every time is what lets a long-running process be told the
38
+ * truth if the environment is re-read.
39
+ */
40
+ function isLogEnabled(pgm_cd, fn_cd, act_id) {
41
+ const on = (s) => (s ? node_util_1.default.debuglog(s).enabled : false);
42
+ return on(pgm_cd) || on(fn_cd) || on(act_id?.toString());
43
+ }
44
+ /**
45
+ * Replace the value of any masked field, at any depth, before it can be rendered.
46
+ *
47
+ * POL-427 (C01). The comparison is case-insensitive and matches a field whose name CONTAINS a
48
+ * masked word, because the field that leaks is rarely named exactly `password` — it is
49
+ * `userPassword`, `db_pwd`, `authToken`. Over-masking costs a reader one lookup; under-masking
50
+ * costs a credential.
51
+ */
52
+ function maskValues(value, mask = config_js_1.DEFAULT_MASK) {
53
+ const hidden = (key) => {
54
+ const k = key.toLowerCase();
55
+ return mask.some((m) => k.includes(m.toLowerCase()));
56
+ };
57
+ const walk = (v, depth) => {
58
+ if (depth > 8 || v === null || typeof v !== "object")
59
+ return v;
60
+ if (Array.isArray(v))
61
+ return v.map((x) => walk(x, depth + 1));
62
+ const out = {};
63
+ for (const [k, val] of Object.entries(v)) {
64
+ out[k] = hidden(k) ? "***" : walk(val, depth + 1);
65
+ }
66
+ return out;
67
+ };
68
+ return walk(value, 0);
69
+ }
70
+ /** Render anything for a log line: masked, depth-limited, and never throwing. */
71
+ function stringify(obj, config) {
72
+ if (obj === null)
73
+ return "null";
74
+ if (obj === undefined)
75
+ return "undefined";
76
+ if (typeof obj !== "object")
77
+ return String(obj);
78
+ try {
79
+ const masked = maskValues(obj, config?.mask ?? config_js_1.DEFAULT_MASK);
80
+ return node_util_1.default.inspect(masked, { ...COMPACT, ...(config?.inspect ?? {}) });
81
+ }
82
+ catch (e) {
83
+ // A logger that throws while rendering takes the program down at the moment it was trying
84
+ // to explain itself. Say what went wrong with the value and keep the line.
85
+ return `<unrenderable: ${e?.message ?? "unknown"}>`;
86
+ }
87
+ }
88
+ /** Give a call its provenance. Padding keeps a wall of lines readable in columns. */
89
+ function toLogEntry(appId, lvl, msg, pgm_cd, fn_cd, act_id, meta) {
90
+ return {
91
+ level: lvl,
92
+ message: msg,
93
+ app_cd: appId,
94
+ pgm_cd: pgm_cd ?? "n/a",
95
+ fn_cd: fn_cd ?? "n/a",
96
+ act_id: act_id ?? 0,
97
+ meta,
98
+ logTime: new Date().toISOString(),
99
+ };
100
+ }
101
+ /**
102
+ * The single line a transport writes.
103
+ *
104
+ * The origin comes FIRST and at a fixed width, because the common act is scanning a column
105
+ * for one module rather than reading prose — which is what a log is for when it is being read
106
+ * at all.
107
+ */
108
+ function formatLogMessage(e, config) {
109
+ const pgm = (e.pgm_cd ?? "n/a").padEnd(34);
110
+ const fn = (e.fn_cd ?? "n/a").padEnd(22);
111
+ const act = String(e.act_id ?? 0).padEnd(4);
112
+ const meta = e.meta === undefined ? "" : ` meta=${stringify(e.meta, config)}`;
113
+ return `${e.logTime ?? ""} ${e.level.padEnd(7)} ${pgm} ${fn} ${act} ${e.message}${meta}`;
114
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerTransport = exports.createApplicationLogger = exports.toLogEntry = exports.stringify = exports.maskValues = exports.isLogEnabled = exports.formatLogMessage = exports.DEFAULT_MASK = exports.withinLevel = exports.logLevelIndex = exports.LogMethod = exports.LogLevelIndex = exports.LogLevelColors = exports.LogLevel = void 0;
4
+ /* -------------------------------------------------
5
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
6
+ * All rights reserved. This code may not be copied
7
+ * or duplicated in whole or part by any means
8
+ * without express prior agreement in writing.
9
+ * -----------------------------------------------*/
10
+ /**
11
+ * `@svayam-opensource/svm-util-log` — the shared logging framework mandated by POL-423,
12
+ * carrying only the dependencies a logger needs.
13
+ *
14
+ * The first package of the `svm-util` split (911#224). It exists because the logger and the
15
+ * API server were one package, so a command-line tool could not obey POL-423 without shipping
16
+ * express, mysql, axios and ajv to every adopter — 675 packages for a log line.
17
+ *
18
+ * PUBLIC SCOPE, deliberately. `@svayam/svm-util` publishes to the internal registry, which
19
+ * adopters of `@svayam-opensource/gov` cannot resolve. A logging mandate that makes the
20
+ * governance CLI uninstallable is a mandate nothing can obey.
21
+ */
22
+ var levels_js_1 = require("./levels.js");
23
+ Object.defineProperty(exports, "LogLevel", { enumerable: true, get: function () { return levels_js_1.LogLevel; } });
24
+ Object.defineProperty(exports, "LogLevelColors", { enumerable: true, get: function () { return levels_js_1.LogLevelColors; } });
25
+ Object.defineProperty(exports, "LogLevelIndex", { enumerable: true, get: function () { return levels_js_1.LogLevelIndex; } });
26
+ Object.defineProperty(exports, "LogMethod", { enumerable: true, get: function () { return levels_js_1.LogMethod; } });
27
+ Object.defineProperty(exports, "logLevelIndex", { enumerable: true, get: function () { return levels_js_1.logLevelIndex; } });
28
+ Object.defineProperty(exports, "withinLevel", { enumerable: true, get: function () { return levels_js_1.withinLevel; } });
29
+ var config_js_1 = require("./config.js");
30
+ Object.defineProperty(exports, "DEFAULT_MASK", { enumerable: true, get: function () { return config_js_1.DEFAULT_MASK; } });
31
+ var format_js_1 = require("./format.js");
32
+ Object.defineProperty(exports, "formatLogMessage", { enumerable: true, get: function () { return format_js_1.formatLogMessage; } });
33
+ Object.defineProperty(exports, "isLogEnabled", { enumerable: true, get: function () { return format_js_1.isLogEnabled; } });
34
+ Object.defineProperty(exports, "maskValues", { enumerable: true, get: function () { return format_js_1.maskValues; } });
35
+ Object.defineProperty(exports, "stringify", { enumerable: true, get: function () { return format_js_1.stringify; } });
36
+ Object.defineProperty(exports, "toLogEntry", { enumerable: true, get: function () { return format_js_1.toLogEntry; } });
37
+ var logger_js_1 = require("./logger.js");
38
+ Object.defineProperty(exports, "createApplicationLogger", { enumerable: true, get: function () { return logger_js_1.createApplicationLogger; } });
39
+ Object.defineProperty(exports, "registerTransport", { enumerable: true, get: function () { return logger_js_1.registerTransport; } });
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /* -------------------------------------------------
3
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
4
+ * All rights reserved. This code may not be copied
5
+ * or duplicated in whole or part by any means
6
+ * without express prior agreement in writing.
7
+ * -----------------------------------------------*/
8
+ /**
9
+ * Levels, and the shape of a log call — extracted from `svm-util/src/svm-util-base.ts`
10
+ * unchanged, so a line written against either library means the same thing.
11
+ *
12
+ * DELIBERATELY DEPENDENCY-FREE. In `svm-util` these live beside `PoolConfig` from mysql,
13
+ * Axios request types, Ajv and Express, in one shared types module — so importing the level
14
+ * enum pulled a web-server stack. That single file is why a CLI could not take the logger
15
+ * (911#224), and separating it is most of this package's reason to exist.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.LogLevelColors = exports.LogLevelIndex = exports.logLevelIndex = exports.LogLevel = exports.LogMethod = void 0;
19
+ exports.withinLevel = withinLevel;
20
+ /** Where a log line can be sent. `database` and `api` are registered by other packages. */
21
+ exports.LogMethod = { console: "console", file: "file", database: "database", api: "api" };
22
+ exports.LogLevel = {
23
+ error: "error", warn: "warn", info: "info", http: "http",
24
+ verbose: "verbose", debug: "debug", silly: "silly",
25
+ };
26
+ /** Ordered least to most verbose; the index IS the threshold comparison. */
27
+ exports.logLevelIndex = [
28
+ exports.LogLevel.error, exports.LogLevel.warn, exports.LogLevel.info, exports.LogLevel.http,
29
+ exports.LogLevel.verbose, exports.LogLevel.debug, exports.LogLevel.silly,
30
+ ];
31
+ exports.LogLevelIndex = { error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6 };
32
+ exports.LogLevelColors = {
33
+ error: "red", warn: "magenta", info: "green", http: "blue",
34
+ verbose: "cyan", debug: "yellow", silly: "gray",
35
+ };
36
+ /** Is `lvl` within the threshold `at`? Both are level names, not numbers, at the call site. */
37
+ function withinLevel(lvl, at) {
38
+ return exports.LogLevelIndex[lvl] <= exports.LogLevelIndex[at];
39
+ }
@@ -0,0 +1,112 @@
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.registerTransport = registerTransport;
7
+ exports.createApplicationLogger = createApplicationLogger;
8
+ /* -------------------------------------------------
9
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
10
+ * All rights reserved. This code may not be copied
11
+ * or duplicated in whole or part by any means
12
+ * without express prior agreement in writing.
13
+ * -----------------------------------------------*/
14
+ /**
15
+ * The application logger — `svm-util`'s, with the console and file transports and nothing else.
16
+ *
17
+ * TRANSPORTS ARE PLUGGABLE, and that is the change that makes this package possible.
18
+ * `svm-util/src/svm-logger.ts` builds every transport itself, so it imports `svm-db.js` for the
19
+ * database one and drags `mysql` into anything that wants to log. Here a transport for a method
20
+ * this package does not implement is looked up in a registry, and the packages that own those
21
+ * dependencies register into it (911#224).
22
+ *
23
+ * `exitOnError: false`, as in the original: a library must never take the host process down
24
+ * because a transport failed. That is doubly true here — the transport most likely to fail is
25
+ * the one writing the file you were going to read afterwards.
26
+ */
27
+ const node_path_1 = __importDefault(require("node:path"));
28
+ const winston_1 = require("winston");
29
+ const winston_daily_rotate_file_1 = __importDefault(require("winston-daily-rotate-file"));
30
+ const config_js_1 = require("./config.js");
31
+ const levels_js_1 = require("./levels.js");
32
+ const format_js_1 = require("./format.js");
33
+ const registry = new Map();
34
+ function registerTransport(method, make) {
35
+ registry.set(method, make);
36
+ }
37
+ function buildTransports(config) {
38
+ const out = [];
39
+ for (const conf of config.methods) {
40
+ if (conf.METHOD === "console") {
41
+ out.push(new winston_1.transports.Console({
42
+ level: conf.LOGLEVEL || levels_js_1.LogLevel.info,
43
+ handleExceptions: true,
44
+ format: winston_1.format.combine(winston_1.format.colorize({ all: true }), winston_1.format.printf((info) => (0, format_js_1.formatLogMessage)(info, config))),
45
+ // Diagnostics belong on STDERR. stdout is a program's ANSWER, and a caller piping it
46
+ // into another command should not receive the log as data.
47
+ stderrLevels: Object.values(levels_js_1.LogLevel),
48
+ }));
49
+ continue;
50
+ }
51
+ if (conf.METHOD === "file") {
52
+ const f = conf;
53
+ const dir = f.DATA_STORE?.dirname ?? config.logDir;
54
+ out.push(new winston_daily_rotate_file_1.default({
55
+ level: f.LOGLEVEL || levels_js_1.LogLevel.info,
56
+ dirname: dir,
57
+ filename: node_path_1.default.join(String(f.DATA_STORE?.filename ?? ""), `%DATE%-${config.appId}.log`),
58
+ datePattern: f.DATA_STORE?.datePattern ?? "YYYY-MM-DD",
59
+ zippedArchive: f.DATA_STORE?.zippedArchive ?? true,
60
+ maxSize: f.DATA_STORE?.maxSize ?? "20m",
61
+ // A log kept forever is a disclosure risk that grows on its own; a fortnight is enough
62
+ // to diagnose something an adopter noticed today.
63
+ maxFiles: f.DATA_STORE?.maxFiles ?? "14d",
64
+ format: winston_1.format.combine(winston_1.format.printf((info) => (0, format_js_1.formatLogMessage)(info, config))),
65
+ }));
66
+ continue;
67
+ }
68
+ const make = registry.get(conf.METHOD);
69
+ if (make)
70
+ out.push(make(conf, config));
71
+ // else: declared but unavailable. Silence here is correct — see registerTransport.
72
+ }
73
+ return out;
74
+ }
75
+ function createApplicationLogger(config) {
76
+ (0, winston_1.addColors)(levels_js_1.LogLevelColors);
77
+ const built = buildTransports(config);
78
+ const winston = (0, winston_1.createLogger)({
79
+ levels: levels_js_1.LogLevelIndex,
80
+ transports: built,
81
+ exitOnError: false,
82
+ });
83
+ const effective = { mask: config_js_1.DEFAULT_MASK, ...config };
84
+ const log = (lvl, msg, pgm_cd, fn_cd, act_id, meta) => {
85
+ // ERRORS AND WARNINGS ALWAYS PASS. Everything below them is opt-in through NODE_DEBUG, so
86
+ // an ordinary run is quiet — but a run that went wrong still said so without anybody
87
+ // having had the foresight to enable anything.
88
+ const always = levels_js_1.LogLevelIndex[lvl] <= levels_js_1.LogLevelIndex[levels_js_1.LogLevel.warn];
89
+ if (!always && !(0, format_js_1.isLogEnabled)(pgm_cd, fn_cd, act_id))
90
+ return;
91
+ winston.log((0, format_js_1.toLogEntry)(effective.appId, lvl, msg, pgm_cd, fn_cd, act_id, meta));
92
+ };
93
+ const toLogObject = () => ({
94
+ APP_ID: effective.appId,
95
+ LOG_DIR: effective.logDir,
96
+ REQUESTED_METHODS: effective.methods.map((m) => m.METHOD),
97
+ ENABLED_TRANSPORTS: winston.transports.map((t) => ({ type: t.constructor.name, level: t.level })),
98
+ MASK: effective.mask,
99
+ });
100
+ return {
101
+ log,
102
+ toLogObject,
103
+ toString: () => JSON.stringify(toLogObject()),
104
+ // A CLI exits in seconds and a rotating file transport writes asynchronously; without a
105
+ // close the last lines of a short run — the ones describing why it ended — are the ones
106
+ // most likely to be lost.
107
+ close: () => { try {
108
+ winston.close();
109
+ }
110
+ catch { /* closing a closed logger is not news */ } },
111
+ };
112
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * What a consumer declares, and what it injects.
3
+ *
4
+ * TWO DEPARTURES FROM `svm-util`, both deliberate.
5
+ *
6
+ * 1. CONFIG IS INJECTED, NEVER GLOBAL. `svm-util`'s `formatLogMessage` reaches a module-level
7
+ * `getSvayamConfiguration()`, which assumes an application initialised its configuration
8
+ * before anything logged. A long-lived API server can promise that. A command-line tool
9
+ * cannot: `gov` has a first run where no configuration exists at all, several entry points,
10
+ * and a lifetime measured in seconds. A library that requires a global to be set up first
11
+ * is a library that throws during the very failure you are trying to log.
12
+ *
13
+ * 2. ONLY console AND file ARE DECLARED HERE. The database and api transports belong to the
14
+ * packages that carry their dependencies, and register themselves (911#224). Declaring
15
+ * them here would put `mysql` and `axios` back in the type graph, which is the thing this
16
+ * package exists to remove.
17
+ */
18
+ import type { logLevelType, logMethodType } from "./levels.js";
19
+ /** Where a rotating file transport writes. Winston's own options, narrowed to what is used. */
20
+ export interface FileLogOptions {
21
+ /** directory; defaults to the application's log directory. */
22
+ dirname?: string;
23
+ /** basename; the date and the app id are added around it. */
24
+ filename?: string;
25
+ datePattern?: string;
26
+ zippedArchive?: boolean;
27
+ maxSize?: string;
28
+ maxFiles?: string;
29
+ [key: string]: unknown;
30
+ }
31
+ export type CONSOLE_LOG_CONFIG_METHOD = {
32
+ METHOD: "console";
33
+ LOGLEVEL: logLevelType;
34
+ };
35
+ export interface FILE_LOG_CONFIG_METHOD {
36
+ METHOD: "file";
37
+ LOGLEVEL: logLevelType;
38
+ DATA_STORE?: FileLogOptions;
39
+ [key: string]: unknown;
40
+ }
41
+ /**
42
+ * A method this package does not implement — `database`, `api`, or anything a future package
43
+ * adds. Carried through the config so a consumer's file does not have to be rewritten when it
44
+ * moves between environments, and IGNORED unless something registered a transport for it.
45
+ */
46
+ export interface EXTERNAL_LOG_CONFIG_METHOD {
47
+ METHOD: Exclude<logMethodType, "console" | "file">;
48
+ LOGLEVEL: logLevelType;
49
+ [key: string]: unknown;
50
+ }
51
+ export type LOG_CONFIG_METHOD = CONSOLE_LOG_CONFIG_METHOD | FILE_LOG_CONFIG_METHOD | EXTERNAL_LOG_CONFIG_METHOD;
52
+ /** Everything the logger needs, handed to it rather than found by it. */
53
+ export interface LoggerConfig {
54
+ /** Names the application in every line and in the log filename. */
55
+ readonly appId: string;
56
+ /** Where file transports write when they do not say otherwise. */
57
+ readonly logDir: string;
58
+ readonly methods: readonly LOG_CONFIG_METHOD[];
59
+ /**
60
+ * Field names to mask wherever they appear in `meta`.
61
+ *
62
+ * POL-427 is C01: no credential, key, token or PII reaches a log, at any level, in any
63
+ * transport. Masking belongs here rather than in a call site's memory — a log is a file, it
64
+ * is copied, and it is sent to other people precisely when something has gone wrong.
65
+ */
66
+ readonly mask?: readonly string[];
67
+ /** Depth and length limits for `meta`, so one object cannot become the whole file. */
68
+ readonly inspect?: {
69
+ depth?: number;
70
+ maxStringLength?: number;
71
+ };
72
+ }
73
+ /** What a `mask` covers before a consumer adds to it. Extend, do not replace. */
74
+ export declare const DEFAULT_MASK: readonly string[];
@@ -0,0 +1,6 @@
1
+ /** What a `mask` covers before a consumer adds to it. Extend, do not replace. */
2
+ export const DEFAULT_MASK = [
3
+ "password", "passwd", "pwd", "secret", "token", "apiKey", "api_key",
4
+ "authorization", "auth", "credential", "credentials", "privateKey", "private_key",
5
+ "sessionId", "session_id", "cookie", "jwt", "bearer",
6
+ ];
@@ -0,0 +1,39 @@
1
+ import type { LoggerConfig } from "./config.js";
2
+ import type { LogEntryFields, logLevelType } from "./levels.js";
3
+ /**
4
+ * Is logging switched on for this origin?
5
+ *
6
+ * `node:util.debuglog` is the switch, so `NODE_DEBUG=gov-work:cli:agent-install` enables one
7
+ * module and `NODE_DEBUG=gov-work:*` the package — POL-425's "switchable at run time by
8
+ * whoever is in front of the problem, with no rebuild and no release".
9
+ *
10
+ * NOT CACHED PER CALL BY ACCIDENT: `debuglog` itself is lazy and memoised, so asking it every
11
+ * time is cheap, and asking it every time is what lets a long-running process be told the
12
+ * truth if the environment is re-read.
13
+ */
14
+ export declare function isLogEnabled(pgm_cd?: string, fn_cd?: string, act_id?: number): boolean;
15
+ /**
16
+ * Replace the value of any masked field, at any depth, before it can be rendered.
17
+ *
18
+ * POL-427 (C01). The comparison is case-insensitive and matches a field whose name CONTAINS a
19
+ * masked word, because the field that leaks is rarely named exactly `password` — it is
20
+ * `userPassword`, `db_pwd`, `authToken`. Over-masking costs a reader one lookup; under-masking
21
+ * costs a credential.
22
+ */
23
+ export declare function maskValues(value: unknown, mask?: readonly string[]): unknown;
24
+ /** Render anything for a log line: masked, depth-limited, and never throwing. */
25
+ export declare function stringify(obj: unknown, config?: Pick<LoggerConfig, "mask" | "inspect">): string;
26
+ /** Give a call its provenance. Padding keeps a wall of lines readable in columns. */
27
+ export declare function toLogEntry(appId: string, lvl: logLevelType, msg: string, pgm_cd?: string, fn_cd?: string, act_id?: number, meta?: unknown): LogEntryFields & {
28
+ logTime: string;
29
+ };
30
+ /**
31
+ * The single line a transport writes.
32
+ *
33
+ * The origin comes FIRST and at a fixed width, because the common act is scanning a column
34
+ * for one module rather than reading prose — which is what a log is for when it is being read
35
+ * at all.
36
+ */
37
+ export declare function formatLogMessage(e: LogEntryFields & {
38
+ logTime?: string;
39
+ }, config?: Pick<LoggerConfig, "mask" | "inspect">): string;
@@ -0,0 +1,104 @@
1
+ /* -------------------------------------------------
2
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
3
+ * All rights reserved. This code may not be copied
4
+ * or duplicated in whole or part by any means
5
+ * without express prior agreement in writing.
6
+ * -----------------------------------------------*/
7
+ /**
8
+ * Turning a call into a line — masking, provenance, and the one-line layout.
9
+ *
10
+ * Extracted from `svm-util/src/svm-helper.ts` (`stringify`, `getLogEntry`, `formatLogMessage`,
11
+ * `isLogEnabled`), with the global configuration lookup removed: every function here takes
12
+ * what it needs. See `config.ts` for why.
13
+ */
14
+ import nodeUtil from "node:util";
15
+ import { DEFAULT_MASK } from "./config.js";
16
+ const COMPACT = {
17
+ depth: 4, compact: true, breakLength: Infinity, maxStringLength: 512, colors: false,
18
+ };
19
+ /**
20
+ * Is logging switched on for this origin?
21
+ *
22
+ * `node:util.debuglog` is the switch, so `NODE_DEBUG=gov-work:cli:agent-install` enables one
23
+ * module and `NODE_DEBUG=gov-work:*` the package — POL-425's "switchable at run time by
24
+ * whoever is in front of the problem, with no rebuild and no release".
25
+ *
26
+ * NOT CACHED PER CALL BY ACCIDENT: `debuglog` itself is lazy and memoised, so asking it every
27
+ * time is cheap, and asking it every time is what lets a long-running process be told the
28
+ * truth if the environment is re-read.
29
+ */
30
+ export function isLogEnabled(pgm_cd, fn_cd, act_id) {
31
+ const on = (s) => (s ? nodeUtil.debuglog(s).enabled : false);
32
+ return on(pgm_cd) || on(fn_cd) || on(act_id?.toString());
33
+ }
34
+ /**
35
+ * Replace the value of any masked field, at any depth, before it can be rendered.
36
+ *
37
+ * POL-427 (C01). The comparison is case-insensitive and matches a field whose name CONTAINS a
38
+ * masked word, because the field that leaks is rarely named exactly `password` — it is
39
+ * `userPassword`, `db_pwd`, `authToken`. Over-masking costs a reader one lookup; under-masking
40
+ * costs a credential.
41
+ */
42
+ export function maskValues(value, mask = DEFAULT_MASK) {
43
+ const hidden = (key) => {
44
+ const k = key.toLowerCase();
45
+ return mask.some((m) => k.includes(m.toLowerCase()));
46
+ };
47
+ const walk = (v, depth) => {
48
+ if (depth > 8 || v === null || typeof v !== "object")
49
+ return v;
50
+ if (Array.isArray(v))
51
+ return v.map((x) => walk(x, depth + 1));
52
+ const out = {};
53
+ for (const [k, val] of Object.entries(v)) {
54
+ out[k] = hidden(k) ? "***" : walk(val, depth + 1);
55
+ }
56
+ return out;
57
+ };
58
+ return walk(value, 0);
59
+ }
60
+ /** Render anything for a log line: masked, depth-limited, and never throwing. */
61
+ export function stringify(obj, config) {
62
+ if (obj === null)
63
+ return "null";
64
+ if (obj === undefined)
65
+ return "undefined";
66
+ if (typeof obj !== "object")
67
+ return String(obj);
68
+ try {
69
+ const masked = maskValues(obj, config?.mask ?? DEFAULT_MASK);
70
+ return nodeUtil.inspect(masked, { ...COMPACT, ...(config?.inspect ?? {}) });
71
+ }
72
+ catch (e) {
73
+ // A logger that throws while rendering takes the program down at the moment it was trying
74
+ // to explain itself. Say what went wrong with the value and keep the line.
75
+ return `<unrenderable: ${e?.message ?? "unknown"}>`;
76
+ }
77
+ }
78
+ /** Give a call its provenance. Padding keeps a wall of lines readable in columns. */
79
+ export function toLogEntry(appId, lvl, msg, pgm_cd, fn_cd, act_id, meta) {
80
+ return {
81
+ level: lvl,
82
+ message: msg,
83
+ app_cd: appId,
84
+ pgm_cd: pgm_cd ?? "n/a",
85
+ fn_cd: fn_cd ?? "n/a",
86
+ act_id: act_id ?? 0,
87
+ meta,
88
+ logTime: new Date().toISOString(),
89
+ };
90
+ }
91
+ /**
92
+ * The single line a transport writes.
93
+ *
94
+ * The origin comes FIRST and at a fixed width, because the common act is scanning a column
95
+ * for one module rather than reading prose — which is what a log is for when it is being read
96
+ * at all.
97
+ */
98
+ export function formatLogMessage(e, config) {
99
+ const pgm = (e.pgm_cd ?? "n/a").padEnd(34);
100
+ const fn = (e.fn_cd ?? "n/a").padEnd(22);
101
+ const act = String(e.act_id ?? 0).padEnd(4);
102
+ const meta = e.meta === undefined ? "" : ` meta=${stringify(e.meta, config)}`;
103
+ return `${e.logTime ?? ""} ${e.level.padEnd(7)} ${pgm} ${fn} ${act} ${e.message}${meta}`;
104
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@svayam-opensource/svm-util-log` — the shared logging framework mandated by POL-423,
3
+ * carrying only the dependencies a logger needs.
4
+ *
5
+ * The first package of the `svm-util` split (911#224). It exists because the logger and the
6
+ * API server were one package, so a command-line tool could not obey POL-423 without shipping
7
+ * express, mysql, axios and ajv to every adopter — 675 packages for a log line.
8
+ *
9
+ * PUBLIC SCOPE, deliberately. `@svayam/svm-util` publishes to the internal registry, which
10
+ * adopters of `@svayam-opensource/gov` cannot resolve. A logging mandate that makes the
11
+ * governance CLI uninstallable is a mandate nothing can obey.
12
+ */
13
+ export { LogLevel, LogLevelColors, LogLevelIndex, LogMethod, logLevelIndex, withinLevel } from "./levels.js";
14
+ export type { LogEntryFields, logFunctionType, logLevelType, logMethodType } from "./levels.js";
15
+ export { DEFAULT_MASK } from "./config.js";
16
+ export type { CONSOLE_LOG_CONFIG_METHOD, EXTERNAL_LOG_CONFIG_METHOD, FILE_LOG_CONFIG_METHOD, FileLogOptions, LOG_CONFIG_METHOD, LoggerConfig, } from "./config.js";
17
+ export { formatLogMessage, isLogEnabled, maskValues, stringify, toLogEntry } from "./format.js";
18
+ export { createApplicationLogger, registerTransport } from "./logger.js";
19
+ export type { ApplicationLogger, TransportFactory } from "./logger.js";
@@ -0,0 +1,22 @@
1
+ /* -------------------------------------------------
2
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
3
+ * All rights reserved. This code may not be copied
4
+ * or duplicated in whole or part by any means
5
+ * without express prior agreement in writing.
6
+ * -----------------------------------------------*/
7
+ /**
8
+ * `@svayam-opensource/svm-util-log` — the shared logging framework mandated by POL-423,
9
+ * carrying only the dependencies a logger needs.
10
+ *
11
+ * The first package of the `svm-util` split (911#224). It exists because the logger and the
12
+ * API server were one package, so a command-line tool could not obey POL-423 without shipping
13
+ * express, mysql, axios and ajv to every adopter — 675 packages for a log line.
14
+ *
15
+ * PUBLIC SCOPE, deliberately. `@svayam/svm-util` publishes to the internal registry, which
16
+ * adopters of `@svayam-opensource/gov` cannot resolve. A logging mandate that makes the
17
+ * governance CLI uninstallable is a mandate nothing can obey.
18
+ */
19
+ export { LogLevel, LogLevelColors, LogLevelIndex, LogMethod, logLevelIndex, withinLevel } from "./levels.js";
20
+ export { DEFAULT_MASK } from "./config.js";
21
+ export { formatLogMessage, isLogEnabled, maskValues, stringify, toLogEntry } from "./format.js";
22
+ export { createApplicationLogger, registerTransport } from "./logger.js";
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Levels, and the shape of a log call — extracted from `svm-util/src/svm-util-base.ts`
3
+ * unchanged, so a line written against either library means the same thing.
4
+ *
5
+ * DELIBERATELY DEPENDENCY-FREE. In `svm-util` these live beside `PoolConfig` from mysql,
6
+ * Axios request types, Ajv and Express, in one shared types module — so importing the level
7
+ * enum pulled a web-server stack. That single file is why a CLI could not take the logger
8
+ * (911#224), and separating it is most of this package's reason to exist.
9
+ */
10
+ /** Where a log line can be sent. `database` and `api` are registered by other packages. */
11
+ export declare const LogMethod: {
12
+ readonly console: "console";
13
+ readonly file: "file";
14
+ readonly database: "database";
15
+ readonly api: "api";
16
+ };
17
+ export type logMethodType = keyof typeof LogMethod;
18
+ export declare const LogLevel: {
19
+ readonly error: "error";
20
+ readonly warn: "warn";
21
+ readonly info: "info";
22
+ readonly http: "http";
23
+ readonly verbose: "verbose";
24
+ readonly debug: "debug";
25
+ readonly silly: "silly";
26
+ };
27
+ export type logLevelType = keyof typeof LogLevel;
28
+ /** Ordered least to most verbose; the index IS the threshold comparison. */
29
+ export declare const logLevelIndex: logLevelType[];
30
+ export declare const LogLevelIndex: {
31
+ readonly error: 0;
32
+ readonly warn: 1;
33
+ readonly info: 2;
34
+ readonly http: 3;
35
+ readonly verbose: 4;
36
+ readonly debug: 5;
37
+ readonly silly: 6;
38
+ };
39
+ export declare const LogLevelColors: {
40
+ error: string;
41
+ warn: string;
42
+ info: string;
43
+ http: string;
44
+ verbose: string;
45
+ debug: string;
46
+ silly: string;
47
+ };
48
+ /**
49
+ * The call every logged line makes.
50
+ *
51
+ * `pgm_cd` and `fn_cd` are not decoration — they are how a reader gets from a line back to the
52
+ * code that wrote it (POL-426). The convention is the CODE PATH: `<package>:<dir>:<module>`,
53
+ * so `gov-work:cli:agent-install` is `src/cli/agent-install.ts` and nothing has to be guessed.
54
+ *
55
+ * `act_id` ties lines from one unit of work together across modules.
56
+ */
57
+ export type logFunctionType = (lvl: logLevelType, msg: string, pgm_cd?: string, fn_cd?: string, act_id?: number, meta?: unknown) => void;
58
+ /** A line, once it has been given its provenance. */
59
+ export interface LogEntryFields {
60
+ level: logLevelType;
61
+ message: string;
62
+ pgm_cd?: string;
63
+ fn_cd?: string;
64
+ app_cd?: string;
65
+ act_id?: number;
66
+ meta?: unknown;
67
+ }
68
+ /** Is `lvl` within the threshold `at`? Both are level names, not numbers, at the call site. */
69
+ export declare function withinLevel(lvl: logLevelType, at: logLevelType): boolean;
@@ -0,0 +1,35 @@
1
+ /* -------------------------------------------------
2
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
3
+ * All rights reserved. This code may not be copied
4
+ * or duplicated in whole or part by any means
5
+ * without express prior agreement in writing.
6
+ * -----------------------------------------------*/
7
+ /**
8
+ * Levels, and the shape of a log call — extracted from `svm-util/src/svm-util-base.ts`
9
+ * unchanged, so a line written against either library means the same thing.
10
+ *
11
+ * DELIBERATELY DEPENDENCY-FREE. In `svm-util` these live beside `PoolConfig` from mysql,
12
+ * Axios request types, Ajv and Express, in one shared types module — so importing the level
13
+ * enum pulled a web-server stack. That single file is why a CLI could not take the logger
14
+ * (911#224), and separating it is most of this package's reason to exist.
15
+ */
16
+ /** Where a log line can be sent. `database` and `api` are registered by other packages. */
17
+ export const LogMethod = { console: "console", file: "file", database: "database", api: "api" };
18
+ export const LogLevel = {
19
+ error: "error", warn: "warn", info: "info", http: "http",
20
+ verbose: "verbose", debug: "debug", silly: "silly",
21
+ };
22
+ /** Ordered least to most verbose; the index IS the threshold comparison. */
23
+ export const logLevelIndex = [
24
+ LogLevel.error, LogLevel.warn, LogLevel.info, LogLevel.http,
25
+ LogLevel.verbose, LogLevel.debug, LogLevel.silly,
26
+ ];
27
+ export const LogLevelIndex = { error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6 };
28
+ export const LogLevelColors = {
29
+ error: "red", warn: "magenta", info: "green", http: "blue",
30
+ verbose: "cyan", debug: "yellow", silly: "gray",
31
+ };
32
+ /** Is `lvl` within the threshold `at`? Both are level names, not numbers, at the call site. */
33
+ export function withinLevel(lvl, at) {
34
+ return LogLevelIndex[lvl] <= LogLevelIndex[at];
35
+ }
@@ -0,0 +1,22 @@
1
+ import type Transport from "winston-transport";
2
+ import type { LoggerConfig, LOG_CONFIG_METHOD } from "./config.js";
3
+ import { type logFunctionType, type logLevelType, type logMethodType } from "./levels.js";
4
+ /**
5
+ * Transports this package does not implement, contributed by packages that do.
6
+ *
7
+ * Registration is by METHOD name, so a consumer's config file is unchanged whether or not the
8
+ * contributing package is installed — a `database` method simply does nothing until
9
+ * `@svayam/svm-util-db` is present and has registered. A missing transport is a line that does
10
+ * not go somewhere, never a crash at startup.
11
+ */
12
+ export type TransportFactory = (conf: LOG_CONFIG_METHOD, config: LoggerConfig) => Transport;
13
+ export declare function registerTransport(method: logMethodType, make: TransportFactory): void;
14
+ export interface ApplicationLogger {
15
+ readonly log: logFunctionType;
16
+ /** What is actually enabled, for `gov doctor`-style reporting and for tests. */
17
+ toLogObject(): Record<string, unknown>;
18
+ toString(): string;
19
+ close(): void;
20
+ }
21
+ export declare function createApplicationLogger(config: LoggerConfig): ApplicationLogger;
22
+ export type { logLevelType, logFunctionType };
@@ -0,0 +1,105 @@
1
+ /* -------------------------------------------------
2
+ * Copyright (c) 2026 - Svayam Infoware Pvt. Ltd.
3
+ * All rights reserved. This code may not be copied
4
+ * or duplicated in whole or part by any means
5
+ * without express prior agreement in writing.
6
+ * -----------------------------------------------*/
7
+ /**
8
+ * The application logger — `svm-util`'s, with the console and file transports and nothing else.
9
+ *
10
+ * TRANSPORTS ARE PLUGGABLE, and that is the change that makes this package possible.
11
+ * `svm-util/src/svm-logger.ts` builds every transport itself, so it imports `svm-db.js` for the
12
+ * database one and drags `mysql` into anything that wants to log. Here a transport for a method
13
+ * this package does not implement is looked up in a registry, and the packages that own those
14
+ * dependencies register into it (911#224).
15
+ *
16
+ * `exitOnError: false`, as in the original: a library must never take the host process down
17
+ * because a transport failed. That is doubly true here — the transport most likely to fail is
18
+ * the one writing the file you were going to read afterwards.
19
+ */
20
+ import path from "node:path";
21
+ import { createLogger, format, transports as winstonTransports, addColors } from "winston";
22
+ import DailyRotateFile from "winston-daily-rotate-file";
23
+ import { DEFAULT_MASK } from "./config.js";
24
+ import { LogLevel, LogLevelColors, LogLevelIndex } from "./levels.js";
25
+ import { formatLogMessage, isLogEnabled, toLogEntry } from "./format.js";
26
+ const registry = new Map();
27
+ export function registerTransport(method, make) {
28
+ registry.set(method, make);
29
+ }
30
+ function buildTransports(config) {
31
+ const out = [];
32
+ for (const conf of config.methods) {
33
+ if (conf.METHOD === "console") {
34
+ out.push(new winstonTransports.Console({
35
+ level: conf.LOGLEVEL || LogLevel.info,
36
+ handleExceptions: true,
37
+ format: format.combine(format.colorize({ all: true }), format.printf((info) => formatLogMessage(info, config))),
38
+ // Diagnostics belong on STDERR. stdout is a program's ANSWER, and a caller piping it
39
+ // into another command should not receive the log as data.
40
+ stderrLevels: Object.values(LogLevel),
41
+ }));
42
+ continue;
43
+ }
44
+ if (conf.METHOD === "file") {
45
+ const f = conf;
46
+ const dir = f.DATA_STORE?.dirname ?? config.logDir;
47
+ out.push(new DailyRotateFile({
48
+ level: f.LOGLEVEL || LogLevel.info,
49
+ dirname: dir,
50
+ filename: path.join(String(f.DATA_STORE?.filename ?? ""), `%DATE%-${config.appId}.log`),
51
+ datePattern: f.DATA_STORE?.datePattern ?? "YYYY-MM-DD",
52
+ zippedArchive: f.DATA_STORE?.zippedArchive ?? true,
53
+ maxSize: f.DATA_STORE?.maxSize ?? "20m",
54
+ // A log kept forever is a disclosure risk that grows on its own; a fortnight is enough
55
+ // to diagnose something an adopter noticed today.
56
+ maxFiles: f.DATA_STORE?.maxFiles ?? "14d",
57
+ format: format.combine(format.printf((info) => formatLogMessage(info, config))),
58
+ }));
59
+ continue;
60
+ }
61
+ const make = registry.get(conf.METHOD);
62
+ if (make)
63
+ out.push(make(conf, config));
64
+ // else: declared but unavailable. Silence here is correct — see registerTransport.
65
+ }
66
+ return out;
67
+ }
68
+ export function createApplicationLogger(config) {
69
+ addColors(LogLevelColors);
70
+ const built = buildTransports(config);
71
+ const winston = createLogger({
72
+ levels: LogLevelIndex,
73
+ transports: built,
74
+ exitOnError: false,
75
+ });
76
+ const effective = { mask: DEFAULT_MASK, ...config };
77
+ const log = (lvl, msg, pgm_cd, fn_cd, act_id, meta) => {
78
+ // ERRORS AND WARNINGS ALWAYS PASS. Everything below them is opt-in through NODE_DEBUG, so
79
+ // an ordinary run is quiet — but a run that went wrong still said so without anybody
80
+ // having had the foresight to enable anything.
81
+ const always = LogLevelIndex[lvl] <= LogLevelIndex[LogLevel.warn];
82
+ if (!always && !isLogEnabled(pgm_cd, fn_cd, act_id))
83
+ return;
84
+ winston.log(toLogEntry(effective.appId, lvl, msg, pgm_cd, fn_cd, act_id, meta));
85
+ };
86
+ const toLogObject = () => ({
87
+ APP_ID: effective.appId,
88
+ LOG_DIR: effective.logDir,
89
+ REQUESTED_METHODS: effective.methods.map((m) => m.METHOD),
90
+ ENABLED_TRANSPORTS: winston.transports.map((t) => ({ type: t.constructor.name, level: t.level })),
91
+ MASK: effective.mask,
92
+ });
93
+ return {
94
+ log,
95
+ toLogObject,
96
+ toString: () => JSON.stringify(toLogObject()),
97
+ // A CLI exits in seconds and a rotating file transport writes asynchronously; without a
98
+ // close the last lines of a short run — the ones describing why it ended — are the ones
99
+ // most likely to be lost.
100
+ close: () => { try {
101
+ winston.close();
102
+ }
103
+ catch { /* closing a closed logger is not news */ } },
104
+ };
105
+ }
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@svayam-opensource/svm-util-log",
3
+ "version": "1.0.0",
4
+ "description": "Svayam's shared logging framework — the logger from svm-util, with only the dependencies a logger needs.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./lib/esm/index.d.ts",
9
+ "import": "./lib/esm/index.js",
10
+ "require": "./lib/cjs/index.js"
11
+ }
12
+ },
13
+ "main": "./lib/cjs/index.js",
14
+ "module": "./lib/esm/index.js",
15
+ "types": "./lib/esm/index.d.ts",
16
+ "files": [
17
+ "lib/",
18
+ "README.md"
19
+ ],
20
+ "engines": {
21
+ "node": ">=20.0.0"
22
+ },
23
+ "license": "SEE LICENSE IN LICENSE",
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/Svayamtech/911-SVM-LIB-SVC.git",
30
+ "directory": "packages/libraries/ts/svm-util-log"
31
+ },
32
+ "scripts": {
33
+ "clean": "rm -rf lib",
34
+ "build": "npm run clean && tsc -p tsconfig.json && tsc -p tsconfig-cjs.json",
35
+ "test": "NODE_OPTIONS='--import tsx' mocha --extensions ts 'test/**/*.test.ts'",
36
+ "prepublishOnly": "npm run build && npm run test"
37
+ },
38
+ "dependencies": {
39
+ "winston": "^3.19.0",
40
+ "winston-daily-rotate-file": "^5.0.0",
41
+ "winston-transport": "^4.9.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/chai": "^5.0.1",
45
+ "@types/mocha": "^10.0.10",
46
+ "@types/node": "^22.10.2",
47
+ "chai": "^5.1.2",
48
+ "mocha": "^11.0.1",
49
+ "tsx": "^4.19.2",
50
+ "typescript": "^5.7.2"
51
+ },
52
+ "gov": {
53
+ "contentSha": "4b8408bbbe485fc7feb93faad9f8642ff260fb193d76e20561f5913f6ee1fcc8",
54
+ "shaFrom": "built-ref",
55
+ "commit": "2aec38c1b7ec1dc9f658f76e8a90b7c70bb54d21"
56
+ }
57
+ }