kayvee 3.18.0 → 4.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 +147 -202
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/kayvee.d.ts +12 -0
- package/dist/kayvee.d.ts.map +1 -0
- package/{build/lib → dist}/kayvee.js +17 -30
- package/dist/logger/logger.d.ts +49 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/{build/lib → dist}/logger/logger.js +91 -83
- package/dist/middleware.d.ts +23 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +196 -0
- package/dist/package.json +89 -0
- package/dist/router/index.d.ts +23 -0
- package/dist/router/index.d.ts.map +1 -0
- package/{build/lib → dist}/router/index.js +33 -45
- package/package.json +63 -27
- package/.circleci/config.yml +0 -25
- package/.eslintrc.js +0 -124
- package/.github/workflows/notify-ci-status.yml +0 -20
- package/.nvmrc +0 -1
- package/.prettierrc.json +0 -1
- package/Makefile +0 -55
- package/benchmarks/data/.keep +0 -1
- package/benchmarks/data/corpus-basic.json +0 -22
- package/benchmarks/data/corpus-pathological.json +0 -22
- package/benchmarks/data/corpus-realistic.json +0 -22
- package/benchmarks/data/kvconfig-basic.yml +0 -7
- package/benchmarks/data/kvconfig-pathological.yml +0 -222
- package/benchmarks/data/kvconfig-realistic.yml +0 -39
- package/benchmarks/routing.js +0 -116
- package/build/lib/logger/helpers.js +0 -0
- package/build/lib/middleware.js +0 -274
- package/build/package.json +0 -53
- package/build/test/context_logger.js +0 -69
- package/build/test/kayvee.js +0 -36
- package/build/test/logger_test.js +0 -345
- package/build/test/middleware.js +0 -556
- package/build/test/router.js +0 -451
- package/index.js +0 -7
- package/lib/kayvee.ts +0 -73
- package/lib/logger/helpers.ts +0 -0
- package/lib/logger/logger.ts +0 -312
- package/lib/middleware.ts +0 -317
- package/lib/router/index.ts +0 -234
- package/lib/router/schema_definitions.json +0 -158
- package/test/context_logger.ts +0 -76
- package/test/kayvee.ts +0 -50
- package/test/kvconfig.yml +0 -14
- package/test/logger_test.ts +0 -378
- package/test/middleware.ts +0 -632
- package/test/router.ts +0 -558
- package/test/static/empty.css +0 -0
- package/test/static/js/empty.js +0 -0
- package/test/tests.json +0 -100
- package/tsconfig.json +0 -10
- /package/{build/lib → dist}/router/schema_definitions.json +0 -0
|
@@ -1,7 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
var
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.Logger = void 0;
|
|
37
|
+
exports.setGlobalRouting = setGlobalRouting;
|
|
38
|
+
exports.getGlobalRouter = getGlobalRouter;
|
|
39
|
+
exports.mockRouting = mockRouting;
|
|
40
|
+
const kv = __importStar(require("../kayvee"));
|
|
41
|
+
const router_1 = require("../router");
|
|
42
|
+
const LEVELS = {
|
|
5
43
|
Trace: "trace",
|
|
6
44
|
Debug: "debug",
|
|
7
45
|
Info: "info",
|
|
@@ -9,7 +47,7 @@ var LEVELS = {
|
|
|
9
47
|
Error: "error",
|
|
10
48
|
Critical: "critical",
|
|
11
49
|
};
|
|
12
|
-
|
|
50
|
+
const LOG_LEVEL_ENUM = {
|
|
13
51
|
trace: 0,
|
|
14
52
|
debug: 1,
|
|
15
53
|
info: 2,
|
|
@@ -17,51 +55,51 @@ var LOG_LEVEL_ENUM = {
|
|
|
17
55
|
error: 4,
|
|
18
56
|
critical: 5,
|
|
19
57
|
};
|
|
20
|
-
const assign = Object.assign || _.assign; // Use the faster Object.assign if possible
|
|
21
58
|
let globalRouter;
|
|
22
59
|
function setGlobalRouting(filename) {
|
|
23
|
-
globalRouter = new
|
|
60
|
+
globalRouter = new router_1.Router();
|
|
24
61
|
globalRouter.loadConfig(filename);
|
|
25
62
|
}
|
|
26
63
|
function getGlobalRouter() {
|
|
27
64
|
return globalRouter;
|
|
28
65
|
}
|
|
29
|
-
// This is a port from kayvee-go/logger/logger.go
|
|
30
66
|
class Logger {
|
|
67
|
+
static Trace = LEVELS.Trace;
|
|
68
|
+
static Debug = LEVELS.Debug;
|
|
69
|
+
static Info = LEVELS.Info;
|
|
70
|
+
static Warning = LEVELS.Warning;
|
|
71
|
+
static Error = LEVELS.Error;
|
|
72
|
+
static Critical = LEVELS.Critical;
|
|
73
|
+
static LEVELS = ["trace", "debug", "info", "warn", "error", "critical"];
|
|
74
|
+
static METRICS = ["counter", "gauge"];
|
|
75
|
+
formatter;
|
|
76
|
+
logLvl;
|
|
77
|
+
globals;
|
|
78
|
+
logWriter;
|
|
79
|
+
logRouter;
|
|
80
|
+
asyncLocalStorage;
|
|
31
81
|
constructor(source, logLvl = process.env.KAYVEE_LOG_LEVEL, formatter = kv.format, output = console.error) {
|
|
32
|
-
this.formatter = null;
|
|
33
|
-
this.logLvl = null;
|
|
34
|
-
this.globals = null;
|
|
35
|
-
this.logWriter = null;
|
|
36
|
-
this.logRouter = null;
|
|
37
|
-
this.asyncLocalStorage = null;
|
|
38
82
|
this.formatter = formatter;
|
|
39
83
|
this.logLvl = this._validateLogLvl(logLvl);
|
|
40
84
|
this.globals = {};
|
|
41
85
|
this.globals.source = source;
|
|
42
86
|
this.logWriter = output;
|
|
87
|
+
this.logRouter = null;
|
|
43
88
|
this.asyncLocalStorage = null;
|
|
44
|
-
if (process.env._TEAM_OWNER)
|
|
89
|
+
if (process.env._TEAM_OWNER)
|
|
45
90
|
this.globals.team = process.env._TEAM_OWNER;
|
|
46
|
-
|
|
47
|
-
if (process.env._DEPLOY_ENV) {
|
|
91
|
+
if (process.env._DEPLOY_ENV)
|
|
48
92
|
this.globals.deploy_env = process.env._DEPLOY_ENV;
|
|
49
|
-
|
|
50
|
-
if (process.env._EXECUTION_NAME) {
|
|
93
|
+
if (process.env._EXECUTION_NAME)
|
|
51
94
|
this.globals.wf_id = process.env._EXECUTION_NAME;
|
|
52
|
-
|
|
53
|
-
if (process.env._POD_ID) {
|
|
95
|
+
if (process.env._POD_ID)
|
|
54
96
|
this.globals["pod-id"] = process.env._POD_ID;
|
|
55
|
-
|
|
56
|
-
if (process.env._POD_SHORTNAME) {
|
|
97
|
+
if (process.env._POD_SHORTNAME)
|
|
57
98
|
this.globals["pod-shortname"] = process.env._POD_SHORTNAME;
|
|
58
|
-
|
|
59
|
-
if (process.env._POD_REGION) {
|
|
99
|
+
if (process.env._POD_REGION)
|
|
60
100
|
this.globals["pod-region"] = process.env._POD_REGION;
|
|
61
|
-
|
|
62
|
-
if (process.env._POD_ACCOUNT) {
|
|
101
|
+
if (process.env._POD_ACCOUNT)
|
|
63
102
|
this.globals["pod-account"] = process.env._POD_ACCOUNT;
|
|
64
|
-
}
|
|
65
103
|
}
|
|
66
104
|
setAsyncLocalStorage(asyncLocalStorage) {
|
|
67
105
|
this.asyncLocalStorage = asyncLocalStorage;
|
|
@@ -77,16 +115,11 @@ class Logger {
|
|
|
77
115
|
return this.logWriter;
|
|
78
116
|
}
|
|
79
117
|
_validateLogLvl(logLvl) {
|
|
80
|
-
if (logLvl == null)
|
|
118
|
+
if (logLvl == null)
|
|
81
119
|
return LEVELS.Debug;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
var value = LEVELS[key];
|
|
86
|
-
if (logLvl.toLowerCase() === value) {
|
|
87
|
-
return value;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
120
|
+
for (const value of Object.values(LEVELS)) {
|
|
121
|
+
if (logLvl.toLowerCase() === value)
|
|
122
|
+
return value;
|
|
90
123
|
}
|
|
91
124
|
return LEVELS.Debug;
|
|
92
125
|
}
|
|
@@ -127,61 +160,37 @@ class Logger {
|
|
|
127
160
|
this.gaugeD(title, value, {});
|
|
128
161
|
}
|
|
129
162
|
traceD(title, data) {
|
|
130
|
-
this._logWithLevel(LEVELS.Trace, {
|
|
131
|
-
title,
|
|
132
|
-
}, data);
|
|
163
|
+
this._logWithLevel(LEVELS.Trace, { title }, data);
|
|
133
164
|
}
|
|
134
165
|
debugD(title, data) {
|
|
135
|
-
this._logWithLevel(LEVELS.Debug, {
|
|
136
|
-
title,
|
|
137
|
-
}, data);
|
|
166
|
+
this._logWithLevel(LEVELS.Debug, { title }, data);
|
|
138
167
|
}
|
|
139
168
|
infoD(title, data) {
|
|
140
|
-
this._logWithLevel(LEVELS.Info, {
|
|
141
|
-
title,
|
|
142
|
-
}, data);
|
|
169
|
+
this._logWithLevel(LEVELS.Info, { title }, data);
|
|
143
170
|
}
|
|
144
171
|
warnD(title, data) {
|
|
145
|
-
this._logWithLevel(LEVELS.Warning, {
|
|
146
|
-
title,
|
|
147
|
-
}, data);
|
|
172
|
+
this._logWithLevel(LEVELS.Warning, { title }, data);
|
|
148
173
|
}
|
|
149
174
|
errorD(title, data) {
|
|
150
|
-
this._logWithLevel(LEVELS.Error, {
|
|
151
|
-
title,
|
|
152
|
-
}, data);
|
|
175
|
+
this._logWithLevel(LEVELS.Error, { title }, data);
|
|
153
176
|
}
|
|
154
177
|
criticalD(title, data) {
|
|
155
|
-
this._logWithLevel(LEVELS.Critical, {
|
|
156
|
-
title,
|
|
157
|
-
}, data);
|
|
178
|
+
this._logWithLevel(LEVELS.Critical, { title }, data);
|
|
158
179
|
}
|
|
159
180
|
counterD(title, value, data) {
|
|
160
|
-
this._logWithLevel(LEVELS.Info, {
|
|
161
|
-
title,
|
|
162
|
-
value,
|
|
163
|
-
type: "counter",
|
|
164
|
-
}, data);
|
|
181
|
+
this._logWithLevel(LEVELS.Info, { title, value, type: "counter" }, data);
|
|
165
182
|
}
|
|
166
183
|
gaugeD(title, value, data) {
|
|
167
|
-
this._logWithLevel(LEVELS.Info, {
|
|
168
|
-
title,
|
|
169
|
-
value,
|
|
170
|
-
type: "gauge",
|
|
171
|
-
}, data);
|
|
184
|
+
this._logWithLevel(LEVELS.Info, { title, value, type: "gauge" }, data);
|
|
172
185
|
}
|
|
173
186
|
_logWithLevel(logLvl, metadata, userdata) {
|
|
174
|
-
if (LOG_LEVEL_ENUM[logLvl] < LOG_LEVEL_ENUM[this.logLvl])
|
|
187
|
+
if (LOG_LEVEL_ENUM[logLvl] < LOG_LEVEL_ENUM[this.logLvl])
|
|
175
188
|
return;
|
|
176
|
-
}
|
|
177
|
-
// I'm not clever enough to want to do these in one line without extra vars.
|
|
178
|
-
// We're on a REALLY old version of TS compiling to ES5. So I don't get a lot of the fancy tools
|
|
179
|
-
// like ?. and ??.
|
|
180
189
|
const store = this.asyncLocalStorage && this.asyncLocalStorage.getStore();
|
|
181
190
|
const storeData = store || { get: () => ({}) };
|
|
182
191
|
const contextData = storeData.get("context") ? storeData.get("context") : {};
|
|
183
192
|
const plainContextData = contextData instanceof Map ? Object.fromEntries(contextData) : contextData;
|
|
184
|
-
|
|
193
|
+
const data = Object.assign({ level: logLvl }, this.globals, metadata, plainContextData, userdata);
|
|
185
194
|
if (this.logRouter) {
|
|
186
195
|
data._kvmeta = this.logRouter.route(data);
|
|
187
196
|
}
|
|
@@ -191,10 +200,8 @@ class Logger {
|
|
|
191
200
|
this.logWriter(this.formatter(data));
|
|
192
201
|
}
|
|
193
202
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
module.exports.getGlobalRouter = getGlobalRouter;
|
|
197
|
-
module.exports.mockRouting = (cb) => {
|
|
203
|
+
exports.Logger = Logger;
|
|
204
|
+
function mockRouting(cb) {
|
|
198
205
|
const _logWithLevel = Logger.prototype._logWithLevel;
|
|
199
206
|
if (_logWithLevel.isMocked) {
|
|
200
207
|
throw Error("Nested kv.mockRouting calls are not supported");
|
|
@@ -205,9 +212,8 @@ module.exports.mockRouting = (cb) => {
|
|
|
205
212
|
const logWriter = this.logWriter;
|
|
206
213
|
this.formatter = (msg) => msg;
|
|
207
214
|
this.logWriter = (msg) => {
|
|
208
|
-
if (!msg._kvmeta)
|
|
215
|
+
if (!msg._kvmeta)
|
|
209
216
|
return;
|
|
210
|
-
}
|
|
211
217
|
msg._kvmeta.routes.forEach((route) => {
|
|
212
218
|
ruleMatches[route.rule] = (ruleMatches[route.rule] || []).concat(route);
|
|
213
219
|
});
|
|
@@ -216,14 +222,16 @@ module.exports.mockRouting = (cb) => {
|
|
|
216
222
|
this.formatter = formatter;
|
|
217
223
|
this.logWriter = logWriter;
|
|
218
224
|
};
|
|
219
|
-
|
|
220
|
-
stfuTypeScript.isMocked = true;
|
|
225
|
+
Logger.prototype._logWithLevel.isMocked = true;
|
|
221
226
|
const done = () => {
|
|
222
227
|
Logger.prototype._logWithLevel = _logWithLevel;
|
|
223
228
|
return ruleMatches;
|
|
224
229
|
};
|
|
225
230
|
cb(done);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
231
|
+
}
|
|
232
|
+
// Expose module-level functions as static members on Logger for backwards compatibility.
|
|
233
|
+
// Tests and consumers that do `const { Logger: KV } = require("kayvee/logger")`
|
|
234
|
+
// can call `KV.setGlobalRouting(...)` and `KV.mockRouting(...)`.
|
|
235
|
+
Logger.setGlobalRouting = setGlobalRouting;
|
|
236
|
+
Logger.getGlobalRouter = getGlobalRouter;
|
|
237
|
+
Logger.mockRouting = mockRouting;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Request, Response, NextFunction } from "express";
|
|
2
|
+
type Handler = (req: Request, res?: Response) => Record<string, unknown>;
|
|
3
|
+
export declare class ContextLogger {
|
|
4
|
+
logger: any;
|
|
5
|
+
handlers: Handler[];
|
|
6
|
+
req: Request;
|
|
7
|
+
res?: Response;
|
|
8
|
+
constructor(logger: any, handlers: Handler[], req: Request, res?: Response);
|
|
9
|
+
_contextualData(data: Record<string, unknown>): Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface MiddlewareOptions {
|
|
12
|
+
source: string;
|
|
13
|
+
headers?: string[];
|
|
14
|
+
handlers?: Handler[];
|
|
15
|
+
base_handlers?: Handler[];
|
|
16
|
+
ignore_dir?: {
|
|
17
|
+
directory: string;
|
|
18
|
+
path: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function middleware(clever_options: MiddlewareOptions, secondOpt?: any): (req: Request, res: Response, next: NextFunction) => void;
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../lib/middleware.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAyE/D,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAoCzE,qBAAa,aAAa;IACxB,MAAM,EAAE,GAAG,CAAC;IACZ,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,GAAG,EAAE,OAAO,CAAC;IACb,GAAG,CAAC,EAAE,QAAQ,CAAC;gBAEH,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,QAAQ;IAO1E,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGxE;AA2BD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,EAAE,CAAC;IAC1B,UAAU,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AA0CD,wBAAgB,UAAU,CACxB,cAAc,EAAE,iBAAiB,EACjC,SAAS,CAAC,EAAE,GAAG,GACd,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,KAAK,IAAI,CAkC3D"}
|
|
@@ -0,0 +1,196 @@
|
|
|
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.ContextLogger = void 0;
|
|
7
|
+
exports.middleware = middleware;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_url_1 = require("node:url");
|
|
11
|
+
const morgan_1 = __importDefault(require("morgan"));
|
|
12
|
+
const qs_1 = __importDefault(require("qs"));
|
|
13
|
+
const kayvee_1 = require("./kayvee");
|
|
14
|
+
const logger_1 = require("./logger/logger");
|
|
15
|
+
function walkDirSync(dir, files = []) {
|
|
16
|
+
const list = node_fs_1.default.readdirSync(dir);
|
|
17
|
+
list.forEach((file) => {
|
|
18
|
+
const f = node_path_1.default.join(dir, file);
|
|
19
|
+
if (node_fs_1.default.statSync(node_path_1.default.join(dir, file)).isDirectory()) {
|
|
20
|
+
walkDirSync(f, files);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
files.push(f);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
return files.map((f) => node_path_1.default.relative(dir, f));
|
|
27
|
+
}
|
|
28
|
+
function skip_path(dir, base_path = "/") {
|
|
29
|
+
let files = walkDirSync(dir);
|
|
30
|
+
files = files.map((file) => node_path_1.default.join(base_path, file));
|
|
31
|
+
console.error(`KayveeMiddleware: Skipping successful requests for files in ${dir} at ${base_path}`);
|
|
32
|
+
return (req, res) => files.includes(req.path) && res.statusCode < 400;
|
|
33
|
+
}
|
|
34
|
+
function getBaseUrl(req) {
|
|
35
|
+
const url = req.originalUrl || req.url;
|
|
36
|
+
const parsed = (0, node_url_1.parse)(url, true);
|
|
37
|
+
return parsed.pathname ?? null;
|
|
38
|
+
}
|
|
39
|
+
function getQueryParams(req) {
|
|
40
|
+
const url = req.originalUrl || req.url;
|
|
41
|
+
const parsed = (0, node_url_1.parse)(url, true);
|
|
42
|
+
const parsedQueryString = qs_1.default.parse(parsed.search ?? "", {
|
|
43
|
+
allowPrototypes: false,
|
|
44
|
+
ignoreQueryPrefix: true,
|
|
45
|
+
});
|
|
46
|
+
return `?${qs_1.default.stringify(parsedQueryString)}`;
|
|
47
|
+
}
|
|
48
|
+
function getResponseSize(res) {
|
|
49
|
+
const headers = res.getHeaders ? res.getHeaders() : res._headers;
|
|
50
|
+
if (headers && headers["content-length"]) {
|
|
51
|
+
return Number(headers["content-length"]);
|
|
52
|
+
}
|
|
53
|
+
else if (res.data) {
|
|
54
|
+
return res.data.length;
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
function getResponseTimeNs(req, res) {
|
|
59
|
+
if (!req._startAt || !res._startAt) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const ns = (res._startAt[0] - req._startAt[0]) * 1e9 +
|
|
63
|
+
(res._startAt[1] - req._startAt[1]);
|
|
64
|
+
return ns;
|
|
65
|
+
}
|
|
66
|
+
function getIp(req) {
|
|
67
|
+
const remoteAddress = req.connection ? req.connection.remoteAddress : undefined;
|
|
68
|
+
return req.ip || remoteAddress;
|
|
69
|
+
}
|
|
70
|
+
function getLogLevel(req, res) {
|
|
71
|
+
const statusCode = res.statusCode;
|
|
72
|
+
if (statusCode >= 499) {
|
|
73
|
+
return logger_1.Logger.Error;
|
|
74
|
+
}
|
|
75
|
+
return logger_1.Logger.Info;
|
|
76
|
+
}
|
|
77
|
+
const defaultHandlers = [
|
|
78
|
+
(req) => ({ method: req.method }),
|
|
79
|
+
(req) => ({ path: getBaseUrl(req) }),
|
|
80
|
+
(req) => ({ params: getQueryParams(req) }),
|
|
81
|
+
(req, res) => ({ "response-size": getResponseSize(res) }),
|
|
82
|
+
(req, res) => ({ "response-time": getResponseTimeNs(req, res) }),
|
|
83
|
+
(req, res) => ({ "status-code": res.statusCode }),
|
|
84
|
+
(req) => ({ ip: getIp(req) }),
|
|
85
|
+
() => ({ via: "kayvee-middleware" }),
|
|
86
|
+
(req, res) => ({ level: getLogLevel(req, res) }),
|
|
87
|
+
() => ({ title: "request-finished" }),
|
|
88
|
+
];
|
|
89
|
+
const defaultContextHandlers = [];
|
|
90
|
+
function handlerData(handlers, req, res) {
|
|
91
|
+
const data = {};
|
|
92
|
+
handlers.forEach((h) => {
|
|
93
|
+
try {
|
|
94
|
+
const handler_data = h(req, res);
|
|
95
|
+
if (handler_data !== null &&
|
|
96
|
+
typeof handler_data === "object" &&
|
|
97
|
+
!Array.isArray(handler_data)) {
|
|
98
|
+
Object.assign(data, handler_data);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
// swallow invalid handler
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return data;
|
|
106
|
+
}
|
|
107
|
+
class ContextLogger {
|
|
108
|
+
logger;
|
|
109
|
+
handlers;
|
|
110
|
+
req;
|
|
111
|
+
res;
|
|
112
|
+
constructor(logger, handlers, req, res) {
|
|
113
|
+
this.logger = logger;
|
|
114
|
+
this.handlers = handlers;
|
|
115
|
+
this.req = req;
|
|
116
|
+
this.res = res;
|
|
117
|
+
}
|
|
118
|
+
_contextualData(data) {
|
|
119
|
+
return Object.assign(handlerData(this.handlers, this.req, this.res), data);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
exports.ContextLogger = ContextLogger;
|
|
123
|
+
for (const func of logger_1.Logger.LEVELS) {
|
|
124
|
+
ContextLogger.prototype[func] = function (title) {
|
|
125
|
+
this[`${func}D`](title, {});
|
|
126
|
+
};
|
|
127
|
+
ContextLogger.prototype[`${func}D`] = function (title, data) {
|
|
128
|
+
this.logger[`${func}D`](title, this._contextualData(data));
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
for (const func of logger_1.Logger.METRICS) {
|
|
132
|
+
ContextLogger.prototype[func] = function (title, value) {
|
|
133
|
+
this[`${func}D`](title, value, {});
|
|
134
|
+
};
|
|
135
|
+
ContextLogger.prototype[`${func}D`] = function (title, value, data) {
|
|
136
|
+
this.logger[`${func}D`](title, value, this._contextualData(data));
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
const formatLine = (options_arg) => {
|
|
140
|
+
const options = options_arg || {};
|
|
141
|
+
if (!options.source) {
|
|
142
|
+
throw Error("Missing required config for 'source' in Kayvee middleware 'options'");
|
|
143
|
+
}
|
|
144
|
+
const router = (0, logger_1.getGlobalRouter)();
|
|
145
|
+
return (_tokens, req, res) => {
|
|
146
|
+
const data = {};
|
|
147
|
+
const custom_headers = options.headers || [];
|
|
148
|
+
const header_data = {};
|
|
149
|
+
custom_headers.forEach((h) => {
|
|
150
|
+
const lc = h.toLowerCase();
|
|
151
|
+
header_data[lc] = req.headers[lc];
|
|
152
|
+
});
|
|
153
|
+
Object.assign(data, header_data);
|
|
154
|
+
const custom_handlers = options.handlers || [];
|
|
155
|
+
let base_handlers = options.base_handlers || defaultHandlers;
|
|
156
|
+
base_handlers = base_handlers.concat([() => ({ source: options.source })]);
|
|
157
|
+
const all_handlers = custom_handlers.concat(base_handlers);
|
|
158
|
+
Object.assign(data, handlerData(all_handlers, req, res));
|
|
159
|
+
if (router) {
|
|
160
|
+
data._kvmeta = router.route(data);
|
|
161
|
+
}
|
|
162
|
+
return (0, kayvee_1.format)(data);
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
const defaultContextLoggerOpts = {
|
|
166
|
+
enabled: true,
|
|
167
|
+
handlers: defaultContextHandlers,
|
|
168
|
+
};
|
|
169
|
+
function middleware(clever_options, secondOpt) {
|
|
170
|
+
if (process.env.NODE_ENV === "test") {
|
|
171
|
+
const morgan_options = secondOpt || { skip: null };
|
|
172
|
+
if (clever_options.ignore_dir) {
|
|
173
|
+
morgan_options.skip = skip_path(clever_options.ignore_dir.directory, clever_options.ignore_dir.path);
|
|
174
|
+
}
|
|
175
|
+
return (0, morgan_1.default)(formatLine(clever_options), morgan_options);
|
|
176
|
+
}
|
|
177
|
+
if (!clever_options.source) {
|
|
178
|
+
throw new Error("Missing required config for 'source' in Kayvee middleware 'options'");
|
|
179
|
+
}
|
|
180
|
+
const context_logger_options = secondOpt || defaultContextLoggerOpts;
|
|
181
|
+
const logger = new logger_1.Logger(clever_options.source);
|
|
182
|
+
const morgan_options = {
|
|
183
|
+
stream: process.stderr,
|
|
184
|
+
skip: null,
|
|
185
|
+
};
|
|
186
|
+
if (clever_options.ignore_dir) {
|
|
187
|
+
morgan_options.skip = skip_path(clever_options.ignore_dir.directory, clever_options.ignore_dir.path);
|
|
188
|
+
}
|
|
189
|
+
const morgan_logger = (0, morgan_1.default)(formatLine(clever_options), morgan_options);
|
|
190
|
+
return (req, res, next) => {
|
|
191
|
+
if (context_logger_options.enabled) {
|
|
192
|
+
req.log = new ContextLogger(logger, context_logger_options.handlers, req);
|
|
193
|
+
}
|
|
194
|
+
morgan_logger(req, res, next);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kayvee",
|
|
3
|
+
"description": "Write data to key=val pairs, for human and machine readability",
|
|
4
|
+
"version": "4.0.0",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": "./dist/index.js",
|
|
10
|
+
"import": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./logger": {
|
|
14
|
+
"require": "./dist/logger/logger.js",
|
|
15
|
+
"import": "./dist/logger/logger.js",
|
|
16
|
+
"types": "./dist/logger/logger.d.ts"
|
|
17
|
+
},
|
|
18
|
+
"./middleware": {
|
|
19
|
+
"require": "./dist/middleware.js",
|
|
20
|
+
"import": "./dist/middleware.js",
|
|
21
|
+
"types": "./dist/middleware.d.ts"
|
|
22
|
+
},
|
|
23
|
+
"./router": {
|
|
24
|
+
"require": "./dist/router/index.js",
|
|
25
|
+
"import": "./dist/router/index.js",
|
|
26
|
+
"types": "./dist/router/index.d.ts"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"typesVersions": {
|
|
30
|
+
"*": {
|
|
31
|
+
"logger": ["dist/logger/logger.d.ts"],
|
|
32
|
+
"middleware": ["dist/middleware.d.ts"],
|
|
33
|
+
"router": ["dist/router/index.d.ts"]
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist/**/*",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE"
|
|
40
|
+
],
|
|
41
|
+
"repository": {
|
|
42
|
+
"type": "git",
|
|
43
|
+
"url": "git://github.com/Clever/kayvee-js"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"js-yaml": "^4.1.0",
|
|
47
|
+
"jsonschema": "^1.5.0",
|
|
48
|
+
"morgan": "^1.10.0",
|
|
49
|
+
"qs": "^6.13.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@clever/prettier-config": "1.0.0",
|
|
53
|
+
"@types/express": "^4.17.23",
|
|
54
|
+
"@types/js-yaml": "^4.0.9",
|
|
55
|
+
"@types/jsonschema": "^1.1.1",
|
|
56
|
+
"@types/mocha": "^10.0.6",
|
|
57
|
+
"@types/morgan": "^1.9.10",
|
|
58
|
+
"@types/node": "^20.11.0",
|
|
59
|
+
"@types/qs": "^6.9.11",
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
|
61
|
+
"@typescript-eslint/parser": "^6.19.0",
|
|
62
|
+
"benchmark": "^2.1.1",
|
|
63
|
+
"eslint": "^8.56.0",
|
|
64
|
+
"eslint-config-airbnb": "^19.0.4",
|
|
65
|
+
"eslint-config-prettier": "^9.1.0",
|
|
66
|
+
"eslint-formatter-summary": "^1.1.0",
|
|
67
|
+
"express": "^4.18.2",
|
|
68
|
+
"mocha": "^10.2.0",
|
|
69
|
+
"prettier": "^3.2.5",
|
|
70
|
+
"sinon": "^17.0.1",
|
|
71
|
+
"split": "^1.0.0",
|
|
72
|
+
"supertest": "^6.3.4",
|
|
73
|
+
"ts-node": "^10.9.2",
|
|
74
|
+
"typescript": "^5.3.0",
|
|
75
|
+
"underscore": "^1.8.3"
|
|
76
|
+
},
|
|
77
|
+
"scripts": {
|
|
78
|
+
"test": "make -Br test",
|
|
79
|
+
"prepublishOnly": "make build"
|
|
80
|
+
},
|
|
81
|
+
"author": "Clever <tech-notify@clever.com>",
|
|
82
|
+
"license": "BSD-2-Clause",
|
|
83
|
+
"bugs": {
|
|
84
|
+
"url": "https://github.com/Clever/kayvee-js/issues"
|
|
85
|
+
},
|
|
86
|
+
"publishConfig": {
|
|
87
|
+
"registry": "https://registry.npmjs.org"
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare class Rule {
|
|
2
|
+
name: string;
|
|
3
|
+
matchers: Record<string, string[]>;
|
|
4
|
+
output: Record<string, unknown>;
|
|
5
|
+
constructor(name: string, matchers: Record<string, string[]>, output: Record<string, unknown>);
|
|
6
|
+
matches(msg: Record<string, unknown>): boolean;
|
|
7
|
+
outputFor(msg: Record<string, unknown>): Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
interface RouteResult {
|
|
10
|
+
team: string;
|
|
11
|
+
kv_version: string;
|
|
12
|
+
kv_language: string;
|
|
13
|
+
routes: Record<string, unknown>[];
|
|
14
|
+
}
|
|
15
|
+
export declare class Router {
|
|
16
|
+
rules: Rule[];
|
|
17
|
+
constructor(rules?: Rule[]);
|
|
18
|
+
loadConfig(filename: string): void;
|
|
19
|
+
_loadConfigString(configStr: string): void;
|
|
20
|
+
route(msg: Record<string, unknown>): RouteResult;
|
|
21
|
+
}
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/router/index.ts"],"names":[],"mappings":"AA2EA,qBAAa,IAAI;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAEpB,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAkC7F,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO;IAS9C,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAsBjE;AA4CD,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACnC;AAED,qBAAa,MAAM;IACjB,KAAK,EAAE,IAAI,EAAE,CAAC;gBAEF,KAAK,CAAC,EAAE,IAAI,EAAE;IAI1B,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAKlC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAQ1C,KAAK,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,WAAW;CAgBjD"}
|