apitally 0.6.1 → 0.8.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/dist/common/client.cjs +559 -0
- package/dist/common/client.cjs.map +1 -0
- package/dist/common/client.d.cts +38 -0
- package/dist/common/client.d.ts +38 -0
- package/dist/common/client.js +526 -0
- package/dist/common/client.js.map +1 -0
- package/dist/common/consumerRegistry.cjs +86 -0
- package/dist/common/consumerRegistry.cjs.map +1 -0
- package/dist/common/consumerRegistry.d.cts +14 -0
- package/dist/common/consumerRegistry.d.ts +14 -0
- package/dist/common/consumerRegistry.js +63 -0
- package/dist/common/consumerRegistry.js.map +1 -0
- package/dist/common/logging.cjs +41 -0
- package/dist/common/logging.cjs.map +1 -0
- package/dist/common/logging.d.cts +11 -0
- package/{lib/types → dist}/common/logging.d.ts +6 -3
- package/dist/common/logging.js +18 -0
- package/dist/common/logging.js.map +1 -0
- package/dist/common/packageVersions.cjs +42 -0
- package/dist/common/packageVersions.cjs.map +1 -0
- package/dist/common/packageVersions.d.cts +3 -0
- package/dist/common/packageVersions.d.ts +3 -0
- package/dist/common/packageVersions.js +18 -0
- package/dist/common/packageVersions.js.map +1 -0
- package/dist/common/paramValidation.cjs +43 -0
- package/dist/common/paramValidation.cjs.map +1 -0
- package/dist/common/paramValidation.d.cts +4 -0
- package/dist/common/paramValidation.d.ts +4 -0
- package/dist/common/paramValidation.js +19 -0
- package/dist/common/paramValidation.js.map +1 -0
- package/dist/common/requestCounter.cjs +111 -0
- package/dist/common/requestCounter.cjs.map +1 -0
- package/dist/common/requestCounter.d.cts +18 -0
- package/{lib/types → dist}/common/requestCounter.d.ts +7 -3
- package/dist/common/requestCounter.js +92 -0
- package/dist/common/requestCounter.js.map +1 -0
- package/dist/common/serverErrorCounter.cjs +133 -0
- package/dist/common/serverErrorCounter.cjs.map +1 -0
- package/dist/common/serverErrorCounter.d.cts +20 -0
- package/{lib/types → dist}/common/serverErrorCounter.d.ts +7 -3
- package/dist/common/serverErrorCounter.js +104 -0
- package/dist/common/serverErrorCounter.js.map +1 -0
- package/dist/common/types.cjs +19 -0
- package/dist/common/types.cjs.map +1 -0
- package/dist/common/types.d.cts +83 -0
- package/{lib/types → dist}/common/types.d.ts +25 -15
- package/dist/common/types.js +1 -0
- package/dist/common/types.js.map +1 -0
- package/dist/common/validationErrorCounter.cjs +76 -0
- package/dist/common/validationErrorCounter.cjs.map +1 -0
- package/dist/common/validationErrorCounter.d.cts +14 -0
- package/{lib/types → dist}/common/validationErrorCounter.d.ts +7 -3
- package/dist/common/validationErrorCounter.js +57 -0
- package/dist/common/validationErrorCounter.js.map +1 -0
- package/dist/express/index.cjs +898 -0
- package/dist/express/index.cjs.map +1 -0
- package/dist/express/index.d.cts +5 -0
- package/dist/express/index.d.ts +5 -0
- package/dist/express/index.js +862 -0
- package/dist/express/index.js.map +1 -0
- package/dist/express/listEndpoints.cjs +144 -0
- package/dist/express/listEndpoints.cjs.map +1 -0
- package/dist/express/listEndpoints.d.cts +3 -0
- package/dist/express/listEndpoints.d.ts +3 -0
- package/dist/express/listEndpoints.js +125 -0
- package/dist/express/listEndpoints.js.map +1 -0
- package/dist/express/middleware.cjs +896 -0
- package/dist/express/middleware.cjs.map +1 -0
- package/dist/express/middleware.d.cts +14 -0
- package/dist/express/middleware.d.ts +14 -0
- package/dist/express/middleware.js +862 -0
- package/dist/express/middleware.js.map +1 -0
- package/dist/fastify/index.cjs +754 -0
- package/dist/fastify/index.cjs.map +1 -0
- package/dist/fastify/index.d.cts +5 -0
- package/dist/fastify/index.d.ts +5 -0
- package/dist/fastify/index.js +718 -0
- package/dist/fastify/index.js.map +1 -0
- package/dist/fastify/plugin.cjs +748 -0
- package/dist/fastify/plugin.cjs.map +1 -0
- package/dist/fastify/plugin.d.cts +18 -0
- package/dist/fastify/plugin.d.ts +18 -0
- package/dist/fastify/plugin.js +718 -0
- package/dist/fastify/plugin.js.map +1 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -0
- package/dist/koa/index.cjs +718 -0
- package/dist/koa/index.cjs.map +1 -0
- package/dist/koa/index.d.cts +5 -0
- package/dist/koa/index.d.ts +5 -0
- package/dist/koa/index.js +682 -0
- package/dist/koa/index.js.map +1 -0
- package/dist/koa/middleware.cjs +718 -0
- package/dist/koa/middleware.cjs.map +1 -0
- package/dist/koa/middleware.d.cts +8 -0
- package/dist/koa/middleware.d.ts +8 -0
- package/dist/koa/middleware.js +682 -0
- package/dist/koa/middleware.js.map +1 -0
- package/dist/nestjs/index.cjs +898 -0
- package/dist/nestjs/index.cjs.map +1 -0
- package/dist/nestjs/index.d.cts +5 -0
- package/dist/nestjs/index.d.ts +5 -0
- package/dist/nestjs/index.js +862 -0
- package/dist/nestjs/index.js.map +1 -0
- package/package.json +55 -92
- package/lib/cjs/common/client.cjs +0 -334
- package/lib/cjs/common/client.cjs.map +0 -1
- package/lib/cjs/common/logging.cjs +0 -17
- package/lib/cjs/common/logging.cjs.map +0 -1
- package/lib/cjs/common/packageVersions.cjs +0 -16
- package/lib/cjs/common/packageVersions.cjs.map +0 -1
- package/lib/cjs/common/paramValidation.cjs +0 -16
- package/lib/cjs/common/paramValidation.cjs.map +0 -1
- package/lib/cjs/common/requestCounter.cjs +0 -108
- package/lib/cjs/common/requestCounter.cjs.map +0 -1
- package/lib/cjs/common/serverErrorCounter.cjs +0 -154
- package/lib/cjs/common/serverErrorCounter.cjs.map +0 -1
- package/lib/cjs/common/types.cjs +0 -6
- package/lib/cjs/common/types.cjs.map +0 -1
- package/lib/cjs/common/validationErrorCounter.cjs +0 -60
- package/lib/cjs/common/validationErrorCounter.cjs.map +0 -1
- package/lib/cjs/express/index.cjs +0 -13
- package/lib/cjs/express/index.cjs.map +0 -1
- package/lib/cjs/express/listEndpoints.cjs +0 -177
- package/lib/cjs/express/listEndpoints.cjs.map +0 -1
- package/lib/cjs/express/middleware.cjs +0 -189
- package/lib/cjs/express/middleware.cjs.map +0 -1
- package/lib/cjs/fastify/index.cjs +0 -14
- package/lib/cjs/fastify/index.cjs.map +0 -1
- package/lib/cjs/fastify/plugin.cjs +0 -171
- package/lib/cjs/fastify/plugin.cjs.map +0 -1
- package/lib/cjs/index.cjs +0 -6
- package/lib/cjs/index.cjs.map +0 -1
- package/lib/cjs/koa/index.cjs +0 -13
- package/lib/cjs/koa/index.cjs.map +0 -1
- package/lib/cjs/koa/middleware.cjs +0 -139
- package/lib/cjs/koa/middleware.cjs.map +0 -1
- package/lib/cjs/nestjs/index.cjs +0 -13
- package/lib/cjs/nestjs/index.cjs.map +0 -1
- package/lib/esm/common/client.js +0 -324
- package/lib/esm/common/client.js.map +0 -1
- package/lib/esm/common/logging.js +0 -11
- package/lib/esm/common/logging.js.map +0 -1
- package/lib/esm/common/packageVersions.js +0 -10
- package/lib/esm/common/packageVersions.js.map +0 -1
- package/lib/esm/common/paramValidation.js +0 -9
- package/lib/esm/common/paramValidation.js.map +0 -1
- package/lib/esm/common/requestCounter.js +0 -102
- package/lib/esm/common/requestCounter.js.map +0 -1
- package/lib/esm/common/serverErrorCounter.js +0 -143
- package/lib/esm/common/serverErrorCounter.js.map +0 -1
- package/lib/esm/common/types.js +0 -1
- package/lib/esm/common/types.js.map +0 -1
- package/lib/esm/common/validationErrorCounter.js +0 -54
- package/lib/esm/common/validationErrorCounter.js.map +0 -1
- package/lib/esm/express/index.js +0 -1
- package/lib/esm/express/index.js.map +0 -1
- package/lib/esm/express/listEndpoints.js +0 -170
- package/lib/esm/express/listEndpoints.js.map +0 -1
- package/lib/esm/express/middleware.js +0 -182
- package/lib/esm/express/middleware.js.map +0 -1
- package/lib/esm/fastify/index.js +0 -1
- package/lib/esm/fastify/index.js.map +0 -1
- package/lib/esm/fastify/plugin.js +0 -164
- package/lib/esm/fastify/plugin.js.map +0 -1
- package/lib/esm/index.js +0 -1
- package/lib/esm/index.js.map +0 -1
- package/lib/esm/koa/index.js +0 -1
- package/lib/esm/koa/index.js.map +0 -1
- package/lib/esm/koa/middleware.js +0 -132
- package/lib/esm/koa/middleware.js.map +0 -1
- package/lib/esm/nestjs/index.js +0 -1
- package/lib/esm/nestjs/index.js.map +0 -1
- package/lib/types/common/client.d.ts +0 -33
- package/lib/types/common/client.d.ts.map +0 -1
- package/lib/types/common/logging.d.ts.map +0 -1
- package/lib/types/common/packageVersions.d.ts +0 -2
- package/lib/types/common/packageVersions.d.ts.map +0 -1
- package/lib/types/common/paramValidation.d.ts +0 -3
- package/lib/types/common/paramValidation.d.ts.map +0 -1
- package/lib/types/common/requestCounter.d.ts.map +0 -1
- package/lib/types/common/serverErrorCounter.d.ts.map +0 -1
- package/lib/types/common/types.d.ts.map +0 -1
- package/lib/types/common/validationErrorCounter.d.ts.map +0 -1
- package/lib/types/express/index.d.ts +0 -2
- package/lib/types/express/index.d.ts.map +0 -1
- package/lib/types/express/listEndpoints.d.ts +0 -3
- package/lib/types/express/listEndpoints.d.ts.map +0 -1
- package/lib/types/express/middleware.d.ts +0 -10
- package/lib/types/express/middleware.d.ts.map +0 -1
- package/lib/types/fastify/index.d.ts +0 -2
- package/lib/types/fastify/index.d.ts.map +0 -1
- package/lib/types/fastify/plugin.d.ts +0 -15
- package/lib/types/fastify/plugin.d.ts.map +0 -1
- package/lib/types/index.d.ts +0 -2
- package/lib/types/index.d.ts.map +0 -1
- package/lib/types/koa/index.d.ts +0 -2
- package/lib/types/koa/index.d.ts.map +0 -1
- package/lib/types/koa/middleware.d.ts +0 -4
- package/lib/types/koa/middleware.d.ts.map +0 -1
- package/lib/types/nestjs/index.d.ts +0 -2
- package/lib/types/nestjs/index.d.ts.map +0 -1
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
4
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
|
+
|
|
6
|
+
// src/express/middleware.ts
|
|
7
|
+
import { performance } from "perf_hooks";
|
|
8
|
+
|
|
9
|
+
// src/common/client.ts
|
|
10
|
+
import { randomUUID } from "crypto";
|
|
11
|
+
import fetchRetry from "fetch-retry";
|
|
12
|
+
|
|
13
|
+
// src/common/consumerRegistry.ts
|
|
14
|
+
var consumerFromStringOrObject = /* @__PURE__ */ __name((consumer) => {
|
|
15
|
+
var _a2, _b;
|
|
16
|
+
if (typeof consumer === "string") {
|
|
17
|
+
consumer = String(consumer).trim().substring(0, 128);
|
|
18
|
+
return consumer ? {
|
|
19
|
+
identifier: consumer
|
|
20
|
+
} : null;
|
|
21
|
+
} else {
|
|
22
|
+
consumer.identifier = String(consumer.identifier).trim().substring(0, 128);
|
|
23
|
+
consumer.name = (_a2 = consumer.name) == null ? void 0 : _a2.trim().substring(0, 64);
|
|
24
|
+
consumer.group = (_b = consumer.group) == null ? void 0 : _b.trim().substring(0, 64);
|
|
25
|
+
return consumer.identifier ? consumer : null;
|
|
26
|
+
}
|
|
27
|
+
}, "consumerFromStringOrObject");
|
|
28
|
+
var _ConsumerRegistry = class _ConsumerRegistry {
|
|
29
|
+
consumers;
|
|
30
|
+
updated;
|
|
31
|
+
constructor() {
|
|
32
|
+
this.consumers = /* @__PURE__ */ new Map();
|
|
33
|
+
this.updated = /* @__PURE__ */ new Set();
|
|
34
|
+
}
|
|
35
|
+
addOrUpdateConsumer(consumer) {
|
|
36
|
+
if (!consumer || !consumer.name && !consumer.group) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const existing = this.consumers.get(consumer.identifier);
|
|
40
|
+
if (!existing) {
|
|
41
|
+
this.consumers.set(consumer.identifier, consumer);
|
|
42
|
+
this.updated.add(consumer.identifier);
|
|
43
|
+
} else {
|
|
44
|
+
if (consumer.name && consumer.name !== existing.name) {
|
|
45
|
+
existing.name = consumer.name;
|
|
46
|
+
this.updated.add(consumer.identifier);
|
|
47
|
+
}
|
|
48
|
+
if (consumer.group && consumer.group !== existing.group) {
|
|
49
|
+
existing.group = consumer.group;
|
|
50
|
+
this.updated.add(consumer.identifier);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
getAndResetUpdatedConsumers() {
|
|
55
|
+
const data = [];
|
|
56
|
+
this.updated.forEach((identifier) => {
|
|
57
|
+
const consumer = this.consumers.get(identifier);
|
|
58
|
+
if (consumer) {
|
|
59
|
+
data.push(consumer);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
this.updated.clear();
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
__name(_ConsumerRegistry, "ConsumerRegistry");
|
|
67
|
+
var ConsumerRegistry = _ConsumerRegistry;
|
|
68
|
+
|
|
69
|
+
// src/common/logging.ts
|
|
70
|
+
import { createLogger, format, transports } from "winston";
|
|
71
|
+
var getLogger = /* @__PURE__ */ __name(() => {
|
|
72
|
+
return createLogger({
|
|
73
|
+
level: process.env.APITALLY_DEBUG ? "debug" : "warn",
|
|
74
|
+
format: format.combine(format.colorize(), format.timestamp(), format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`)),
|
|
75
|
+
transports: [
|
|
76
|
+
new transports.Console()
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
}, "getLogger");
|
|
80
|
+
|
|
81
|
+
// src/common/paramValidation.ts
|
|
82
|
+
function isValidClientId(clientId) {
|
|
83
|
+
const regexExp = /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
84
|
+
return regexExp.test(clientId);
|
|
85
|
+
}
|
|
86
|
+
__name(isValidClientId, "isValidClientId");
|
|
87
|
+
function isValidEnv(env) {
|
|
88
|
+
const regexExp = /^[\w-]{1,32}$/;
|
|
89
|
+
return regexExp.test(env);
|
|
90
|
+
}
|
|
91
|
+
__name(isValidEnv, "isValidEnv");
|
|
92
|
+
|
|
93
|
+
// src/common/requestCounter.ts
|
|
94
|
+
var _RequestCounter = class _RequestCounter {
|
|
95
|
+
requestCounts;
|
|
96
|
+
requestSizeSums;
|
|
97
|
+
responseSizeSums;
|
|
98
|
+
responseTimes;
|
|
99
|
+
requestSizes;
|
|
100
|
+
responseSizes;
|
|
101
|
+
constructor() {
|
|
102
|
+
this.requestCounts = /* @__PURE__ */ new Map();
|
|
103
|
+
this.requestSizeSums = /* @__PURE__ */ new Map();
|
|
104
|
+
this.responseSizeSums = /* @__PURE__ */ new Map();
|
|
105
|
+
this.responseTimes = /* @__PURE__ */ new Map();
|
|
106
|
+
this.requestSizes = /* @__PURE__ */ new Map();
|
|
107
|
+
this.responseSizes = /* @__PURE__ */ new Map();
|
|
108
|
+
}
|
|
109
|
+
getKey(requestInfo) {
|
|
110
|
+
return [
|
|
111
|
+
requestInfo.consumer || "",
|
|
112
|
+
requestInfo.method.toUpperCase(),
|
|
113
|
+
requestInfo.path,
|
|
114
|
+
requestInfo.statusCode
|
|
115
|
+
].join("|");
|
|
116
|
+
}
|
|
117
|
+
addRequest(requestInfo) {
|
|
118
|
+
const key = this.getKey(requestInfo);
|
|
119
|
+
this.requestCounts.set(key, (this.requestCounts.get(key) || 0) + 1);
|
|
120
|
+
if (!this.responseTimes.has(key)) {
|
|
121
|
+
this.responseTimes.set(key, /* @__PURE__ */ new Map());
|
|
122
|
+
}
|
|
123
|
+
const responseTimeMap = this.responseTimes.get(key);
|
|
124
|
+
const responseTimeMsBin = Math.floor(requestInfo.responseTime / 10) * 10;
|
|
125
|
+
responseTimeMap.set(responseTimeMsBin, (responseTimeMap.get(responseTimeMsBin) || 0) + 1);
|
|
126
|
+
if (requestInfo.requestSize !== void 0) {
|
|
127
|
+
requestInfo.requestSize = Number(requestInfo.requestSize);
|
|
128
|
+
this.requestSizeSums.set(key, (this.requestSizeSums.get(key) || 0) + requestInfo.requestSize);
|
|
129
|
+
if (!this.requestSizes.has(key)) {
|
|
130
|
+
this.requestSizes.set(key, /* @__PURE__ */ new Map());
|
|
131
|
+
}
|
|
132
|
+
const requestSizeMap = this.requestSizes.get(key);
|
|
133
|
+
const requestSizeKbBin = Math.floor(requestInfo.requestSize / 1e3);
|
|
134
|
+
requestSizeMap.set(requestSizeKbBin, (requestSizeMap.get(requestSizeKbBin) || 0) + 1);
|
|
135
|
+
}
|
|
136
|
+
if (requestInfo.responseSize !== void 0) {
|
|
137
|
+
requestInfo.responseSize = Number(requestInfo.responseSize);
|
|
138
|
+
this.responseSizeSums.set(key, (this.responseSizeSums.get(key) || 0) + requestInfo.responseSize);
|
|
139
|
+
if (!this.responseSizes.has(key)) {
|
|
140
|
+
this.responseSizes.set(key, /* @__PURE__ */ new Map());
|
|
141
|
+
}
|
|
142
|
+
const responseSizeMap = this.responseSizes.get(key);
|
|
143
|
+
const responseSizeKbBin = Math.floor(requestInfo.responseSize / 1e3);
|
|
144
|
+
responseSizeMap.set(responseSizeKbBin, (responseSizeMap.get(responseSizeKbBin) || 0) + 1);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
getAndResetRequests() {
|
|
148
|
+
const data = [];
|
|
149
|
+
this.requestCounts.forEach((count, key) => {
|
|
150
|
+
const [consumer, method, path, statusCodeStr] = key.split("|");
|
|
151
|
+
const responseTimes = this.responseTimes.get(key) || /* @__PURE__ */ new Map();
|
|
152
|
+
const requestSizes = this.requestSizes.get(key) || /* @__PURE__ */ new Map();
|
|
153
|
+
const responseSizes = this.responseSizes.get(key) || /* @__PURE__ */ new Map();
|
|
154
|
+
data.push({
|
|
155
|
+
consumer: consumer || null,
|
|
156
|
+
method,
|
|
157
|
+
path,
|
|
158
|
+
status_code: parseInt(statusCodeStr),
|
|
159
|
+
request_count: count,
|
|
160
|
+
request_size_sum: this.requestSizeSums.get(key) || 0,
|
|
161
|
+
response_size_sum: this.responseSizeSums.get(key) || 0,
|
|
162
|
+
response_times: Object.fromEntries(responseTimes),
|
|
163
|
+
request_sizes: Object.fromEntries(requestSizes),
|
|
164
|
+
response_sizes: Object.fromEntries(responseSizes)
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
this.requestCounts.clear();
|
|
168
|
+
this.requestSizeSums.clear();
|
|
169
|
+
this.responseSizeSums.clear();
|
|
170
|
+
this.responseTimes.clear();
|
|
171
|
+
this.requestSizes.clear();
|
|
172
|
+
this.responseSizes.clear();
|
|
173
|
+
return data;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
__name(_RequestCounter, "RequestCounter");
|
|
177
|
+
var RequestCounter = _RequestCounter;
|
|
178
|
+
|
|
179
|
+
// src/common/serverErrorCounter.ts
|
|
180
|
+
import { createHash } from "crypto";
|
|
181
|
+
var MAX_MSG_LENGTH = 2048;
|
|
182
|
+
var MAX_STACKTRACE_LENGTH = 65536;
|
|
183
|
+
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
184
|
+
errorCounts;
|
|
185
|
+
errorDetails;
|
|
186
|
+
sentryEventIds;
|
|
187
|
+
sentry;
|
|
188
|
+
constructor() {
|
|
189
|
+
this.errorCounts = /* @__PURE__ */ new Map();
|
|
190
|
+
this.errorDetails = /* @__PURE__ */ new Map();
|
|
191
|
+
this.sentryEventIds = /* @__PURE__ */ new Map();
|
|
192
|
+
this.tryImportSentry();
|
|
193
|
+
}
|
|
194
|
+
addServerError(serverError) {
|
|
195
|
+
const key = this.getKey(serverError);
|
|
196
|
+
if (!this.errorDetails.has(key)) {
|
|
197
|
+
this.errorDetails.set(key, serverError);
|
|
198
|
+
}
|
|
199
|
+
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
200
|
+
this.captureSentryEventId(key);
|
|
201
|
+
}
|
|
202
|
+
getAndResetServerErrors() {
|
|
203
|
+
const data = [];
|
|
204
|
+
this.errorCounts.forEach((count, key) => {
|
|
205
|
+
const serverError = this.errorDetails.get(key);
|
|
206
|
+
if (serverError) {
|
|
207
|
+
data.push({
|
|
208
|
+
consumer: serverError.consumer || null,
|
|
209
|
+
method: serverError.method,
|
|
210
|
+
path: serverError.path,
|
|
211
|
+
type: serverError.type,
|
|
212
|
+
msg: this.getTruncatedMessage(serverError.msg),
|
|
213
|
+
traceback: this.getTruncatedStack(serverError.traceback),
|
|
214
|
+
sentry_event_id: this.sentryEventIds.get(key) || null,
|
|
215
|
+
error_count: count
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
this.errorCounts.clear();
|
|
220
|
+
this.errorDetails.clear();
|
|
221
|
+
return data;
|
|
222
|
+
}
|
|
223
|
+
getKey(serverError) {
|
|
224
|
+
const hashInput = [
|
|
225
|
+
serverError.consumer || "",
|
|
226
|
+
serverError.method.toUpperCase(),
|
|
227
|
+
serverError.path,
|
|
228
|
+
serverError.type,
|
|
229
|
+
serverError.msg.trim(),
|
|
230
|
+
serverError.traceback.trim()
|
|
231
|
+
].join("|");
|
|
232
|
+
return createHash("md5").update(hashInput).digest("hex");
|
|
233
|
+
}
|
|
234
|
+
getTruncatedMessage(msg) {
|
|
235
|
+
msg = msg.trim();
|
|
236
|
+
if (msg.length <= MAX_MSG_LENGTH) {
|
|
237
|
+
return msg;
|
|
238
|
+
}
|
|
239
|
+
const suffix = "... (truncated)";
|
|
240
|
+
const cutoff = MAX_MSG_LENGTH - suffix.length;
|
|
241
|
+
return msg.substring(0, cutoff) + suffix;
|
|
242
|
+
}
|
|
243
|
+
getTruncatedStack(stack) {
|
|
244
|
+
const suffix = "... (truncated) ...";
|
|
245
|
+
const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
|
|
246
|
+
const lines = stack.trim().split("\n");
|
|
247
|
+
const truncatedLines = [];
|
|
248
|
+
let length = 0;
|
|
249
|
+
for (const line of lines) {
|
|
250
|
+
if (length + line.length + 1 > cutoff) {
|
|
251
|
+
truncatedLines.push(suffix);
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
truncatedLines.push(line);
|
|
255
|
+
length += line.length + 1;
|
|
256
|
+
}
|
|
257
|
+
return truncatedLines.join("\n");
|
|
258
|
+
}
|
|
259
|
+
captureSentryEventId(serverErrorKey) {
|
|
260
|
+
if (this.sentry && this.sentry.lastEventId) {
|
|
261
|
+
const eventId = this.sentry.lastEventId();
|
|
262
|
+
if (eventId) {
|
|
263
|
+
this.sentryEventIds.set(serverErrorKey, eventId);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async tryImportSentry() {
|
|
268
|
+
try {
|
|
269
|
+
this.sentry = await import("@sentry/node");
|
|
270
|
+
} catch (e) {
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
__name(_ServerErrorCounter, "ServerErrorCounter");
|
|
275
|
+
var ServerErrorCounter = _ServerErrorCounter;
|
|
276
|
+
|
|
277
|
+
// src/common/validationErrorCounter.ts
|
|
278
|
+
import { createHash as createHash2 } from "crypto";
|
|
279
|
+
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
280
|
+
errorCounts;
|
|
281
|
+
errorDetails;
|
|
282
|
+
constructor() {
|
|
283
|
+
this.errorCounts = /* @__PURE__ */ new Map();
|
|
284
|
+
this.errorDetails = /* @__PURE__ */ new Map();
|
|
285
|
+
}
|
|
286
|
+
addValidationError(validationError) {
|
|
287
|
+
const key = this.getKey(validationError);
|
|
288
|
+
if (!this.errorDetails.has(key)) {
|
|
289
|
+
this.errorDetails.set(key, validationError);
|
|
290
|
+
}
|
|
291
|
+
this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
|
|
292
|
+
}
|
|
293
|
+
getAndResetValidationErrors() {
|
|
294
|
+
const data = [];
|
|
295
|
+
this.errorCounts.forEach((count, key) => {
|
|
296
|
+
const validationError = this.errorDetails.get(key);
|
|
297
|
+
if (validationError) {
|
|
298
|
+
data.push({
|
|
299
|
+
consumer: validationError.consumer || null,
|
|
300
|
+
method: validationError.method,
|
|
301
|
+
path: validationError.path,
|
|
302
|
+
loc: validationError.loc.split("."),
|
|
303
|
+
msg: validationError.msg,
|
|
304
|
+
type: validationError.type,
|
|
305
|
+
error_count: count
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
this.errorCounts.clear();
|
|
310
|
+
this.errorDetails.clear();
|
|
311
|
+
return data;
|
|
312
|
+
}
|
|
313
|
+
getKey(validationError) {
|
|
314
|
+
const hashInput = [
|
|
315
|
+
validationError.consumer || "",
|
|
316
|
+
validationError.method.toUpperCase(),
|
|
317
|
+
validationError.path,
|
|
318
|
+
validationError.loc,
|
|
319
|
+
validationError.msg.trim(),
|
|
320
|
+
validationError.type
|
|
321
|
+
].join("|");
|
|
322
|
+
return createHash2("md5").update(hashInput).digest("hex");
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
__name(_ValidationErrorCounter, "ValidationErrorCounter");
|
|
326
|
+
var ValidationErrorCounter = _ValidationErrorCounter;
|
|
327
|
+
|
|
328
|
+
// src/common/client.ts
|
|
329
|
+
var SYNC_INTERVAL = 6e4;
|
|
330
|
+
var INITIAL_SYNC_INTERVAL = 1e4;
|
|
331
|
+
var INITIAL_SYNC_INTERVAL_DURATION = 36e5;
|
|
332
|
+
var MAX_QUEUE_TIME = 36e5;
|
|
333
|
+
var _a;
|
|
334
|
+
var HTTPError = (_a = class extends Error {
|
|
335
|
+
response;
|
|
336
|
+
constructor(response) {
|
|
337
|
+
const reason = response.status ? `status code ${response.status}` : "an unknown error";
|
|
338
|
+
super(`Request failed with ${reason}`);
|
|
339
|
+
this.response = response;
|
|
340
|
+
}
|
|
341
|
+
}, __name(_a, "HTTPError"), _a);
|
|
342
|
+
var _ApitallyClient = class _ApitallyClient {
|
|
343
|
+
clientId;
|
|
344
|
+
env;
|
|
345
|
+
instanceUuid;
|
|
346
|
+
syncDataQueue;
|
|
347
|
+
syncIntervalId;
|
|
348
|
+
startupData;
|
|
349
|
+
startupDataSent = false;
|
|
350
|
+
requestCounter;
|
|
351
|
+
validationErrorCounter;
|
|
352
|
+
serverErrorCounter;
|
|
353
|
+
consumerRegistry;
|
|
354
|
+
logger;
|
|
355
|
+
constructor({ clientId, env = "dev", logger }) {
|
|
356
|
+
if (_ApitallyClient.instance) {
|
|
357
|
+
throw new Error("Apitally client is already initialized");
|
|
358
|
+
}
|
|
359
|
+
if (!isValidClientId(clientId)) {
|
|
360
|
+
throw new Error(`Invalid client ID '${clientId}' (expecting hexadeciaml UUID format)`);
|
|
361
|
+
}
|
|
362
|
+
if (!isValidEnv(env)) {
|
|
363
|
+
throw new Error(`Invalid env '${env}' (expecting 1-32 alphanumeric lowercase characters and hyphens only)`);
|
|
364
|
+
}
|
|
365
|
+
_ApitallyClient.instance = this;
|
|
366
|
+
this.clientId = clientId;
|
|
367
|
+
this.env = env;
|
|
368
|
+
this.instanceUuid = randomUUID();
|
|
369
|
+
this.syncDataQueue = [];
|
|
370
|
+
this.requestCounter = new RequestCounter();
|
|
371
|
+
this.validationErrorCounter = new ValidationErrorCounter();
|
|
372
|
+
this.serverErrorCounter = new ServerErrorCounter();
|
|
373
|
+
this.consumerRegistry = new ConsumerRegistry();
|
|
374
|
+
this.logger = logger || getLogger();
|
|
375
|
+
this.startSync();
|
|
376
|
+
this.handleShutdown = this.handleShutdown.bind(this);
|
|
377
|
+
}
|
|
378
|
+
static getInstance() {
|
|
379
|
+
if (!_ApitallyClient.instance) {
|
|
380
|
+
throw new Error("Apitally client is not initialized");
|
|
381
|
+
}
|
|
382
|
+
return _ApitallyClient.instance;
|
|
383
|
+
}
|
|
384
|
+
static async shutdown() {
|
|
385
|
+
if (_ApitallyClient.instance) {
|
|
386
|
+
await _ApitallyClient.instance.handleShutdown();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
async handleShutdown() {
|
|
390
|
+
this.stopSync();
|
|
391
|
+
await this.sendSyncData();
|
|
392
|
+
_ApitallyClient.instance = void 0;
|
|
393
|
+
}
|
|
394
|
+
getHubUrlPrefix() {
|
|
395
|
+
const baseURL = process.env.APITALLY_HUB_BASE_URL || "https://hub.apitally.io";
|
|
396
|
+
const version = "v2";
|
|
397
|
+
return `${baseURL}/${version}/${this.clientId}/${this.env}/`;
|
|
398
|
+
}
|
|
399
|
+
async sendData(url, payload) {
|
|
400
|
+
const fetchWithRetry = fetchRetry(fetch, {
|
|
401
|
+
retries: 3,
|
|
402
|
+
retryDelay: 1e3,
|
|
403
|
+
retryOn: [
|
|
404
|
+
408,
|
|
405
|
+
429,
|
|
406
|
+
500,
|
|
407
|
+
502,
|
|
408
|
+
503,
|
|
409
|
+
504
|
|
410
|
+
]
|
|
411
|
+
});
|
|
412
|
+
const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {
|
|
413
|
+
method: "POST",
|
|
414
|
+
body: JSON.stringify(payload),
|
|
415
|
+
headers: {
|
|
416
|
+
"Content-Type": "application/json"
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
if (!response.ok) {
|
|
420
|
+
throw new HTTPError(response);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
startSync() {
|
|
424
|
+
this.sync();
|
|
425
|
+
this.syncIntervalId = setInterval(() => {
|
|
426
|
+
this.sync();
|
|
427
|
+
}, INITIAL_SYNC_INTERVAL);
|
|
428
|
+
setTimeout(() => {
|
|
429
|
+
clearInterval(this.syncIntervalId);
|
|
430
|
+
this.syncIntervalId = setInterval(() => {
|
|
431
|
+
this.sync();
|
|
432
|
+
}, SYNC_INTERVAL);
|
|
433
|
+
}, INITIAL_SYNC_INTERVAL_DURATION);
|
|
434
|
+
}
|
|
435
|
+
async sync() {
|
|
436
|
+
try {
|
|
437
|
+
const promises = [
|
|
438
|
+
this.sendSyncData()
|
|
439
|
+
];
|
|
440
|
+
if (!this.startupDataSent) {
|
|
441
|
+
promises.push(this.sendStartupData());
|
|
442
|
+
}
|
|
443
|
+
await Promise.all(promises);
|
|
444
|
+
} catch (error) {
|
|
445
|
+
this.logger.error("Error while syncing with Apitally Hub", {
|
|
446
|
+
error
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
stopSync() {
|
|
451
|
+
if (this.syncIntervalId) {
|
|
452
|
+
clearInterval(this.syncIntervalId);
|
|
453
|
+
this.syncIntervalId = void 0;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
setStartupData(data) {
|
|
457
|
+
this.startupData = data;
|
|
458
|
+
this.startupDataSent = false;
|
|
459
|
+
this.sendStartupData();
|
|
460
|
+
}
|
|
461
|
+
async sendStartupData() {
|
|
462
|
+
if (this.startupData) {
|
|
463
|
+
this.logger.debug("Sending startup data to Apitally Hub");
|
|
464
|
+
const payload = {
|
|
465
|
+
instance_uuid: this.instanceUuid,
|
|
466
|
+
message_uuid: randomUUID(),
|
|
467
|
+
...this.startupData
|
|
468
|
+
};
|
|
469
|
+
try {
|
|
470
|
+
await this.sendData("startup", payload);
|
|
471
|
+
this.startupDataSent = true;
|
|
472
|
+
} catch (error) {
|
|
473
|
+
const handled = this.handleHubError(error);
|
|
474
|
+
if (!handled) {
|
|
475
|
+
this.logger.error(error.message);
|
|
476
|
+
this.logger.debug("Error while sending startup data to Apitally Hub (will retry)", {
|
|
477
|
+
error
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async sendSyncData() {
|
|
484
|
+
this.logger.debug("Synchronizing data with Apitally Hub");
|
|
485
|
+
const newPayload = {
|
|
486
|
+
time_offset: 0,
|
|
487
|
+
instance_uuid: this.instanceUuid,
|
|
488
|
+
message_uuid: randomUUID(),
|
|
489
|
+
requests: this.requestCounter.getAndResetRequests(),
|
|
490
|
+
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
491
|
+
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
492
|
+
consumers: this.consumerRegistry.getAndResetUpdatedConsumers()
|
|
493
|
+
};
|
|
494
|
+
this.syncDataQueue.push([
|
|
495
|
+
Date.now(),
|
|
496
|
+
newPayload
|
|
497
|
+
]);
|
|
498
|
+
const failedItems = [];
|
|
499
|
+
while (this.syncDataQueue.length > 0) {
|
|
500
|
+
const queueItem = this.syncDataQueue.shift();
|
|
501
|
+
if (queueItem) {
|
|
502
|
+
const [time, payload] = queueItem;
|
|
503
|
+
try {
|
|
504
|
+
const timeOffset = Date.now() - time;
|
|
505
|
+
if (timeOffset <= MAX_QUEUE_TIME) {
|
|
506
|
+
payload.time_offset = timeOffset / 1e3;
|
|
507
|
+
await this.sendData("sync", payload);
|
|
508
|
+
}
|
|
509
|
+
} catch (error) {
|
|
510
|
+
const handled = this.handleHubError(error);
|
|
511
|
+
if (!handled) {
|
|
512
|
+
this.logger.debug("Error while synchronizing data with Apitally Hub (will retry)", {
|
|
513
|
+
error
|
|
514
|
+
});
|
|
515
|
+
failedItems.push(queueItem);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
this.syncDataQueue = failedItems;
|
|
521
|
+
}
|
|
522
|
+
handleHubError(error) {
|
|
523
|
+
if (error instanceof HTTPError) {
|
|
524
|
+
if (error.response.status === 404) {
|
|
525
|
+
this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
|
|
526
|
+
this.stopSync();
|
|
527
|
+
return true;
|
|
528
|
+
}
|
|
529
|
+
if (error.response.status === 422) {
|
|
530
|
+
this.logger.error("Received validation error from Apitally Hub");
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
__name(_ApitallyClient, "ApitallyClient");
|
|
538
|
+
__publicField(_ApitallyClient, "instance");
|
|
539
|
+
var ApitallyClient = _ApitallyClient;
|
|
540
|
+
|
|
541
|
+
// src/common/packageVersions.ts
|
|
542
|
+
import { createRequire } from "module";
|
|
543
|
+
function getPackageVersion(name) {
|
|
544
|
+
try {
|
|
545
|
+
const _require = createRequire(import.meta.url);
|
|
546
|
+
return _require(`${name}/package.json`).version || null;
|
|
547
|
+
} catch (error) {
|
|
548
|
+
return null;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
__name(getPackageVersion, "getPackageVersion");
|
|
552
|
+
|
|
553
|
+
// src/express/listEndpoints.js
|
|
554
|
+
var regExpToParseExpressPathRegExp = /^\/\^\\\/(?:(:?[\w\\.-]*(?:\\\/:?[\w\\.-]*)*)|(\(\?:\(\[\^\\\/]\+\?\)\)))\\\/.*/;
|
|
555
|
+
var regExpToReplaceExpressPathRegExpParams = /\(\?:\(\[\^\\\/]\+\?\)\)/;
|
|
556
|
+
var regexpExpressParamRegexp = /\(\?:\(\[\^\\\/]\+\?\)\)/g;
|
|
557
|
+
var EXPRESS_ROOT_PATH_REGEXP_VALUE = "/^\\/?(?=\\/|$)/i";
|
|
558
|
+
var STACK_ITEM_VALID_NAMES = [
|
|
559
|
+
"router",
|
|
560
|
+
"bound dispatch",
|
|
561
|
+
"mounted_app"
|
|
562
|
+
];
|
|
563
|
+
var getRouteMethods = /* @__PURE__ */ __name(function(route) {
|
|
564
|
+
let methods = Object.keys(route.methods);
|
|
565
|
+
methods = methods.filter((method) => method !== "_all");
|
|
566
|
+
methods = methods.map((method) => method.toUpperCase());
|
|
567
|
+
return methods;
|
|
568
|
+
}, "getRouteMethods");
|
|
569
|
+
var getRouteMiddlewares = /* @__PURE__ */ __name(function(route) {
|
|
570
|
+
return route.stack.map((item) => {
|
|
571
|
+
return item.handle.name || "anonymous";
|
|
572
|
+
});
|
|
573
|
+
}, "getRouteMiddlewares");
|
|
574
|
+
var hasParams = /* @__PURE__ */ __name(function(expressPathRegExp) {
|
|
575
|
+
return regexpExpressParamRegexp.test(expressPathRegExp);
|
|
576
|
+
}, "hasParams");
|
|
577
|
+
var parseExpressRoute = /* @__PURE__ */ __name(function(route, basePath) {
|
|
578
|
+
const paths = [];
|
|
579
|
+
if (Array.isArray(route.path)) {
|
|
580
|
+
paths.push(...route.path);
|
|
581
|
+
} else {
|
|
582
|
+
paths.push(route.path);
|
|
583
|
+
}
|
|
584
|
+
const endpoints = paths.map((path) => {
|
|
585
|
+
const completePath = basePath && path === "/" ? basePath : `${basePath}${path}`;
|
|
586
|
+
const endpoint = {
|
|
587
|
+
path: completePath,
|
|
588
|
+
methods: getRouteMethods(route),
|
|
589
|
+
middlewares: getRouteMiddlewares(route)
|
|
590
|
+
};
|
|
591
|
+
return endpoint;
|
|
592
|
+
});
|
|
593
|
+
return endpoints;
|
|
594
|
+
}, "parseExpressRoute");
|
|
595
|
+
var parseExpressPath = /* @__PURE__ */ __name(function(expressPathRegExp, params) {
|
|
596
|
+
let expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(expressPathRegExp);
|
|
597
|
+
let parsedRegExp = expressPathRegExp.toString();
|
|
598
|
+
let paramIndex = 0;
|
|
599
|
+
while (hasParams(parsedRegExp)) {
|
|
600
|
+
const paramName = params[paramIndex].name;
|
|
601
|
+
const paramId = `:${paramName}`;
|
|
602
|
+
parsedRegExp = parsedRegExp.replace(regExpToReplaceExpressPathRegExpParams, paramId);
|
|
603
|
+
paramIndex++;
|
|
604
|
+
}
|
|
605
|
+
if (parsedRegExp !== expressPathRegExp.toString()) {
|
|
606
|
+
expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp);
|
|
607
|
+
}
|
|
608
|
+
const parsedPath = expressPathRegExpExec[1].replace(/\\\//g, "/");
|
|
609
|
+
return parsedPath;
|
|
610
|
+
}, "parseExpressPath");
|
|
611
|
+
var parseEndpoints = /* @__PURE__ */ __name(function(app, basePath, endpoints) {
|
|
612
|
+
const stack = app.stack || app._router && app._router.stack;
|
|
613
|
+
endpoints = endpoints || [];
|
|
614
|
+
basePath = basePath || "";
|
|
615
|
+
if (!stack) {
|
|
616
|
+
endpoints = addEndpoints(endpoints, [
|
|
617
|
+
{
|
|
618
|
+
path: basePath,
|
|
619
|
+
methods: [],
|
|
620
|
+
middlewares: []
|
|
621
|
+
}
|
|
622
|
+
]);
|
|
623
|
+
} else {
|
|
624
|
+
endpoints = parseStack(stack, basePath, endpoints);
|
|
625
|
+
}
|
|
626
|
+
return endpoints;
|
|
627
|
+
}, "parseEndpoints");
|
|
628
|
+
var addEndpoints = /* @__PURE__ */ __name(function(currentEndpoints, endpointsToAdd) {
|
|
629
|
+
endpointsToAdd.forEach((newEndpoint) => {
|
|
630
|
+
const existingEndpoint = currentEndpoints.find((item) => item.path === newEndpoint.path);
|
|
631
|
+
if (existingEndpoint !== void 0) {
|
|
632
|
+
const newMethods = newEndpoint.methods.filter((method) => !existingEndpoint.methods.includes(method));
|
|
633
|
+
existingEndpoint.methods = existingEndpoint.methods.concat(newMethods);
|
|
634
|
+
} else {
|
|
635
|
+
currentEndpoints.push(newEndpoint);
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
return currentEndpoints;
|
|
639
|
+
}, "addEndpoints");
|
|
640
|
+
var parseStack = /* @__PURE__ */ __name(function(stack, basePath, endpoints) {
|
|
641
|
+
stack.forEach((stackItem) => {
|
|
642
|
+
if (stackItem.route) {
|
|
643
|
+
const newEndpoints = parseExpressRoute(stackItem.route, basePath);
|
|
644
|
+
endpoints = addEndpoints(endpoints, newEndpoints);
|
|
645
|
+
} else if (STACK_ITEM_VALID_NAMES.includes(stackItem.name)) {
|
|
646
|
+
const isExpressPathRegexp = regExpToParseExpressPathRegExp.test(stackItem.regexp);
|
|
647
|
+
let newBasePath = basePath;
|
|
648
|
+
if (isExpressPathRegexp) {
|
|
649
|
+
const parsedPath = parseExpressPath(stackItem.regexp, stackItem.keys);
|
|
650
|
+
newBasePath += `/${parsedPath}`;
|
|
651
|
+
} else if (!stackItem.path && stackItem.regexp && stackItem.regexp.toString() !== EXPRESS_ROOT_PATH_REGEXP_VALUE) {
|
|
652
|
+
const regExpPath = ` RegExp(${stackItem.regexp}) `;
|
|
653
|
+
newBasePath += `/${regExpPath}`;
|
|
654
|
+
}
|
|
655
|
+
endpoints = parseEndpoints(stackItem.handle, newBasePath, endpoints);
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
return endpoints;
|
|
659
|
+
}, "parseStack");
|
|
660
|
+
var getEndpoints = /* @__PURE__ */ __name(function(app) {
|
|
661
|
+
const endpoints = parseEndpoints(app);
|
|
662
|
+
return endpoints.flatMap((route) => route.methods.filter((method) => ![
|
|
663
|
+
"HEAD",
|
|
664
|
+
"OPTIONS"
|
|
665
|
+
].includes(method.toUpperCase())).map((method) => ({
|
|
666
|
+
method,
|
|
667
|
+
path: route.path
|
|
668
|
+
})));
|
|
669
|
+
}, "getEndpoints");
|
|
670
|
+
var listEndpoints_default = getEndpoints;
|
|
671
|
+
|
|
672
|
+
// src/express/middleware.ts
|
|
673
|
+
var useApitally = /* @__PURE__ */ __name((app, config) => {
|
|
674
|
+
const client = new ApitallyClient(config);
|
|
675
|
+
const middleware = getMiddleware(app, client);
|
|
676
|
+
app.use(middleware);
|
|
677
|
+
setTimeout(() => {
|
|
678
|
+
client.setStartupData(getAppInfo(app, config.appVersion));
|
|
679
|
+
}, 1e3);
|
|
680
|
+
}, "useApitally");
|
|
681
|
+
var getMiddleware = /* @__PURE__ */ __name((app, client) => {
|
|
682
|
+
const validatorInstalled = getPackageVersion("express-validator") !== null;
|
|
683
|
+
const celebrateInstalled = getPackageVersion("celebrate") !== null;
|
|
684
|
+
const nestInstalled = getPackageVersion("@nestjs/core") !== null;
|
|
685
|
+
const classValidatorInstalled = getPackageVersion("class-validator") !== null;
|
|
686
|
+
let errorHandlerConfigured = false;
|
|
687
|
+
return (req, res, next) => {
|
|
688
|
+
if (!errorHandlerConfigured) {
|
|
689
|
+
app.use((err, req2, res2, next2) => {
|
|
690
|
+
res2.locals.serverError = err;
|
|
691
|
+
next2(err);
|
|
692
|
+
});
|
|
693
|
+
errorHandlerConfigured = true;
|
|
694
|
+
}
|
|
695
|
+
try {
|
|
696
|
+
const startTime = performance.now();
|
|
697
|
+
const originalJson = res.json;
|
|
698
|
+
res.json = (body) => {
|
|
699
|
+
res.locals.body = body;
|
|
700
|
+
return originalJson.call(res, body);
|
|
701
|
+
};
|
|
702
|
+
res.on("finish", () => {
|
|
703
|
+
try {
|
|
704
|
+
if (req.route) {
|
|
705
|
+
const responseTime = performance.now() - startTime;
|
|
706
|
+
const consumer = getConsumer(req);
|
|
707
|
+
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
708
|
+
client.requestCounter.addRequest({
|
|
709
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
710
|
+
method: req.method,
|
|
711
|
+
path: req.route.path,
|
|
712
|
+
statusCode: res.statusCode,
|
|
713
|
+
responseTime,
|
|
714
|
+
requestSize: req.get("content-length"),
|
|
715
|
+
responseSize: res.get("content-length")
|
|
716
|
+
});
|
|
717
|
+
if ((res.statusCode === 400 || res.statusCode === 422) && res.locals.body) {
|
|
718
|
+
const validationErrors = [];
|
|
719
|
+
if (validatorInstalled) {
|
|
720
|
+
validationErrors.push(...extractExpressValidatorErrors(res.locals.body));
|
|
721
|
+
}
|
|
722
|
+
if (celebrateInstalled) {
|
|
723
|
+
validationErrors.push(...extractCelebrateErrors(res.locals.body));
|
|
724
|
+
}
|
|
725
|
+
if (nestInstalled && classValidatorInstalled) {
|
|
726
|
+
validationErrors.push(...extractNestValidationErrors(res.locals.body));
|
|
727
|
+
}
|
|
728
|
+
validationErrors.forEach((error) => {
|
|
729
|
+
client.validationErrorCounter.addValidationError({
|
|
730
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
731
|
+
method: req.method,
|
|
732
|
+
path: req.route.path,
|
|
733
|
+
...error
|
|
734
|
+
});
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
if (res.statusCode === 500 && res.locals.serverError) {
|
|
738
|
+
const serverError = res.locals.serverError;
|
|
739
|
+
client.serverErrorCounter.addServerError({
|
|
740
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
741
|
+
method: req.method,
|
|
742
|
+
path: req.route.path,
|
|
743
|
+
type: serverError.name,
|
|
744
|
+
msg: serverError.message,
|
|
745
|
+
traceback: serverError.stack || ""
|
|
746
|
+
});
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} catch (error) {
|
|
750
|
+
client.logger.error("Error while logging request in Apitally middleware.", {
|
|
751
|
+
request: req,
|
|
752
|
+
response: res,
|
|
753
|
+
error
|
|
754
|
+
});
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
} catch (error) {
|
|
758
|
+
client.logger.error("Error in Apitally middleware.", {
|
|
759
|
+
request: req,
|
|
760
|
+
response: res,
|
|
761
|
+
error
|
|
762
|
+
});
|
|
763
|
+
} finally {
|
|
764
|
+
next();
|
|
765
|
+
}
|
|
766
|
+
};
|
|
767
|
+
}, "getMiddleware");
|
|
768
|
+
var getConsumer = /* @__PURE__ */ __name((req) => {
|
|
769
|
+
if (req.apitallyConsumer) {
|
|
770
|
+
return consumerFromStringOrObject(req.apitallyConsumer);
|
|
771
|
+
} else if (req.consumerIdentifier) {
|
|
772
|
+
process.emitWarning("The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.", "DeprecationWarning");
|
|
773
|
+
return consumerFromStringOrObject(req.consumerIdentifier);
|
|
774
|
+
}
|
|
775
|
+
return null;
|
|
776
|
+
}, "getConsumer");
|
|
777
|
+
var extractExpressValidatorErrors = /* @__PURE__ */ __name((responseBody) => {
|
|
778
|
+
const errors = [];
|
|
779
|
+
if (responseBody && responseBody.errors && Array.isArray(responseBody.errors)) {
|
|
780
|
+
responseBody.errors.forEach((error) => {
|
|
781
|
+
if (error.location && error.path && error.msg && error.type) {
|
|
782
|
+
errors.push({
|
|
783
|
+
loc: `${error.location}.${error.path}`,
|
|
784
|
+
msg: error.msg,
|
|
785
|
+
type: error.type
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
}
|
|
790
|
+
return errors;
|
|
791
|
+
}, "extractExpressValidatorErrors");
|
|
792
|
+
var extractCelebrateErrors = /* @__PURE__ */ __name((responseBody) => {
|
|
793
|
+
const errors = [];
|
|
794
|
+
if (responseBody && responseBody.validation) {
|
|
795
|
+
Object.values(responseBody.validation).forEach((error) => {
|
|
796
|
+
if (error.source && error.keys && Array.isArray(error.keys) && error.message) {
|
|
797
|
+
error.keys.forEach((key) => {
|
|
798
|
+
errors.push({
|
|
799
|
+
loc: `${error.source}.${key}`,
|
|
800
|
+
msg: subsetJoiMessage(error.message, key),
|
|
801
|
+
type: ""
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
return errors;
|
|
808
|
+
}, "extractCelebrateErrors");
|
|
809
|
+
var extractNestValidationErrors = /* @__PURE__ */ __name((responseBody) => {
|
|
810
|
+
const errors = [];
|
|
811
|
+
if (responseBody && Array.isArray(responseBody.message)) {
|
|
812
|
+
responseBody.message.forEach((message) => {
|
|
813
|
+
errors.push({
|
|
814
|
+
loc: "",
|
|
815
|
+
msg: message,
|
|
816
|
+
type: ""
|
|
817
|
+
});
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
return errors;
|
|
821
|
+
}, "extractNestValidationErrors");
|
|
822
|
+
var subsetJoiMessage = /* @__PURE__ */ __name((message, key) => {
|
|
823
|
+
const messageWithKey = message.split(". ").find((message2) => message2.includes(`"${key}"`));
|
|
824
|
+
return messageWithKey ? messageWithKey : message;
|
|
825
|
+
}, "subsetJoiMessage");
|
|
826
|
+
var getAppInfo = /* @__PURE__ */ __name((app, appVersion) => {
|
|
827
|
+
const versions = [
|
|
828
|
+
[
|
|
829
|
+
"nodejs",
|
|
830
|
+
process.version.replace(/^v/, "")
|
|
831
|
+
]
|
|
832
|
+
];
|
|
833
|
+
const expressVersion = getPackageVersion("express");
|
|
834
|
+
const apitallyVersion = getPackageVersion("../..");
|
|
835
|
+
if (expressVersion) {
|
|
836
|
+
versions.push([
|
|
837
|
+
"express",
|
|
838
|
+
expressVersion
|
|
839
|
+
]);
|
|
840
|
+
}
|
|
841
|
+
if (apitallyVersion) {
|
|
842
|
+
versions.push([
|
|
843
|
+
"apitally",
|
|
844
|
+
apitallyVersion
|
|
845
|
+
]);
|
|
846
|
+
}
|
|
847
|
+
if (appVersion) {
|
|
848
|
+
versions.push([
|
|
849
|
+
"app",
|
|
850
|
+
appVersion
|
|
851
|
+
]);
|
|
852
|
+
}
|
|
853
|
+
return {
|
|
854
|
+
paths: listEndpoints_default(app),
|
|
855
|
+
versions: Object.fromEntries(versions),
|
|
856
|
+
client: "js:express"
|
|
857
|
+
};
|
|
858
|
+
}, "getAppInfo");
|
|
859
|
+
export {
|
|
860
|
+
useApitally
|
|
861
|
+
};
|
|
862
|
+
//# sourceMappingURL=middleware.js.map
|