@extk/expressive 0.1.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 +0 -0
- package/dist/index.d.mts +262 -0
- package/dist/index.d.ts +262 -0
- package/dist/index.js +565 -0
- package/dist/index.mjs +499 -0
- package/package.json +53 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
ApiError: () => ApiError,
|
|
34
|
+
ApiErrorResponse: () => ApiErrorResponse,
|
|
35
|
+
ApiResponse: () => ApiResponse,
|
|
36
|
+
BadRequestError: () => BadRequestError,
|
|
37
|
+
DuplicateError: () => DuplicateError,
|
|
38
|
+
FileTooBigError: () => FileTooBigError,
|
|
39
|
+
ForbiddenError: () => ForbiddenError,
|
|
40
|
+
InternalError: () => InternalError,
|
|
41
|
+
InvalidCredentialsError: () => InvalidCredentialsError,
|
|
42
|
+
InvalidFileTypeError: () => InvalidFileTypeError,
|
|
43
|
+
NotFoundError: () => NotFoundError,
|
|
44
|
+
SWG: () => SWG,
|
|
45
|
+
SchemaValidationError: () => SchemaValidationError,
|
|
46
|
+
TokenExpiredError: () => TokenExpiredError,
|
|
47
|
+
TooManyRequestsError: () => TooManyRequestsError,
|
|
48
|
+
UserUnauthorizedError: () => UserUnauthorizedError,
|
|
49
|
+
bootstrap: () => bootstrap,
|
|
50
|
+
createLogger: () => createLogger,
|
|
51
|
+
createReqSnapshot: () => createReqSnapshot,
|
|
52
|
+
getDefaultConsoleLogger: () => getDefaultConsoleLogger,
|
|
53
|
+
getDefaultFileLogger: () => getDefaultFileLogger,
|
|
54
|
+
getEnvVar: () => getEnvVar,
|
|
55
|
+
getTmpDir: () => getTmpDir,
|
|
56
|
+
getTmpPath: () => getTmpPath,
|
|
57
|
+
isDev: () => isDev,
|
|
58
|
+
isProd: () => isProd,
|
|
59
|
+
parseDefaultPagination: () => parseDefaultPagination,
|
|
60
|
+
parseIdOrFail: () => parseIdOrFail,
|
|
61
|
+
parsePositiveInteger: () => parsePositiveInteger,
|
|
62
|
+
slugify: () => slugify
|
|
63
|
+
});
|
|
64
|
+
module.exports = __toCommonJS(index_exports);
|
|
65
|
+
|
|
66
|
+
// src/expressive.ts
|
|
67
|
+
var import_express = __toESM(require("express"));
|
|
68
|
+
var import_helmet = __toESM(require("helmet"));
|
|
69
|
+
var import_morgan = __toESM(require("morgan"));
|
|
70
|
+
var import_qs = __toESM(require("qs"));
|
|
71
|
+
var import_swagger_ui_express = __toESM(require("swagger-ui-express"));
|
|
72
|
+
|
|
73
|
+
// src/swagger.ts
|
|
74
|
+
var buildSwaggerBuilder = (swaggerDoc) => {
|
|
75
|
+
return {
|
|
76
|
+
swaggerBuilder: () => {
|
|
77
|
+
return {
|
|
78
|
+
withInfo(info) {
|
|
79
|
+
swaggerDoc.info = info;
|
|
80
|
+
return this;
|
|
81
|
+
},
|
|
82
|
+
withServers(servers) {
|
|
83
|
+
swaggerDoc.servers = servers;
|
|
84
|
+
return this;
|
|
85
|
+
},
|
|
86
|
+
withSecuritySchemes(schemes) {
|
|
87
|
+
swaggerDoc.components.securitySchemes = schemes;
|
|
88
|
+
return this;
|
|
89
|
+
},
|
|
90
|
+
withSchemas(schemas) {
|
|
91
|
+
swaggerDoc.components.schemas = schemas;
|
|
92
|
+
return this;
|
|
93
|
+
},
|
|
94
|
+
withDefaultSecurity(globalAuthMethods) {
|
|
95
|
+
swaggerDoc.security = globalAuthMethods;
|
|
96
|
+
return this;
|
|
97
|
+
},
|
|
98
|
+
get() {
|
|
99
|
+
return swaggerDoc;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
var securitySchemes = {
|
|
106
|
+
BasicAuth: () => ({
|
|
107
|
+
type: "http",
|
|
108
|
+
scheme: "basic"
|
|
109
|
+
}),
|
|
110
|
+
BearerAuth: () => ({
|
|
111
|
+
type: "http",
|
|
112
|
+
scheme: "bearer"
|
|
113
|
+
}),
|
|
114
|
+
ApiKeyAuth: (headerName) => ({
|
|
115
|
+
type: "apiKey",
|
|
116
|
+
in: "header",
|
|
117
|
+
name: headerName
|
|
118
|
+
}),
|
|
119
|
+
OpenID: (openIdConnectUrl) => ({
|
|
120
|
+
type: "openIdConnect",
|
|
121
|
+
openIdConnectUrl
|
|
122
|
+
}),
|
|
123
|
+
OAuth2: (authorizationUrl, tokenUrl, scopes) => ({
|
|
124
|
+
type: "oauth2",
|
|
125
|
+
flows: {
|
|
126
|
+
authorizationCode: {
|
|
127
|
+
authorizationUrl,
|
|
128
|
+
tokenUrl,
|
|
129
|
+
scopes
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
};
|
|
134
|
+
var securityRegistry = {};
|
|
135
|
+
var security = (name) => {
|
|
136
|
+
if (!securityRegistry[name]) {
|
|
137
|
+
securityRegistry[name] = { [name]: [] };
|
|
138
|
+
}
|
|
139
|
+
return securityRegistry[name];
|
|
140
|
+
};
|
|
141
|
+
function jsonSchema(schema) {
|
|
142
|
+
return {
|
|
143
|
+
content: {
|
|
144
|
+
"application/json": {
|
|
145
|
+
schema
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function jsonSchemaRef(name) {
|
|
151
|
+
return jsonSchema({ $ref: `#/components/schemas/${name}` });
|
|
152
|
+
}
|
|
153
|
+
function param(inP, id, schema, required = true, description = "", name) {
|
|
154
|
+
return {
|
|
155
|
+
in: inP,
|
|
156
|
+
name: name ?? id,
|
|
157
|
+
desciption: description,
|
|
158
|
+
required,
|
|
159
|
+
schema
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function pathParam(id, schema, required = true, description = "", name) {
|
|
163
|
+
return param("path", id, schema, required, description, name);
|
|
164
|
+
}
|
|
165
|
+
function queryParam(id, schema, required = true, description = "", name) {
|
|
166
|
+
return param("query", id, schema, required, description, name);
|
|
167
|
+
}
|
|
168
|
+
function headerParam(id, schema, required = true, description = "", name) {
|
|
169
|
+
return param("headers", id, schema, required, description, name);
|
|
170
|
+
}
|
|
171
|
+
function convertExpressPath(path2) {
|
|
172
|
+
return path2.replace(/:([a-zA-Z0-9_*]+)/g, "{$1}");
|
|
173
|
+
}
|
|
174
|
+
function tryParsePathParameters(path2) {
|
|
175
|
+
const matches = path2.match(/(?<={)[^}]+(?=})/g);
|
|
176
|
+
if (!matches) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
return matches.map((pp) => pathParam(pp, { type: "string" }));
|
|
180
|
+
}
|
|
181
|
+
var SWG = {
|
|
182
|
+
param,
|
|
183
|
+
pathParam,
|
|
184
|
+
queryParam,
|
|
185
|
+
headerParam,
|
|
186
|
+
jsonSchema,
|
|
187
|
+
jsonSchemaRef,
|
|
188
|
+
security,
|
|
189
|
+
securitySchemes
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// src/expressive.ts
|
|
193
|
+
function buildExpressive(container, swaggerDoc) {
|
|
194
|
+
return {
|
|
195
|
+
expressiveServer(configs) {
|
|
196
|
+
const { options } = configs;
|
|
197
|
+
const app = configs.app ?? (0, import_express.default)();
|
|
198
|
+
app.use((0, import_helmet.default)(options?.helmet ?? {}));
|
|
199
|
+
app.set("query parser", function(str) {
|
|
200
|
+
return import_qs.default.parse(str, { decoder(s) {
|
|
201
|
+
return decodeURIComponent(s);
|
|
202
|
+
} });
|
|
203
|
+
});
|
|
204
|
+
app.use((0, import_morgan.default)(
|
|
205
|
+
options?.morgan?.format ?? ":req[x-real-ip] :method :url :status :res[content-length] - :response-time ms",
|
|
206
|
+
options?.morgan?.options ?? { stream: { write(message) {
|
|
207
|
+
container.logger.info(message.trim());
|
|
208
|
+
} } }
|
|
209
|
+
));
|
|
210
|
+
app.use(configs.swagger.path, import_swagger_ui_express.default.serve, import_swagger_ui_express.default.setup(configs.swagger.doc));
|
|
211
|
+
return app;
|
|
212
|
+
},
|
|
213
|
+
expressiveRouter(configs) {
|
|
214
|
+
const router = import_express.default.Router();
|
|
215
|
+
return {
|
|
216
|
+
router,
|
|
217
|
+
addRoute(context, ...handlers) {
|
|
218
|
+
router[context.method](context.path, ...handlers);
|
|
219
|
+
const {
|
|
220
|
+
pathOverride,
|
|
221
|
+
pathParameters,
|
|
222
|
+
headerParameters,
|
|
223
|
+
queryParameters,
|
|
224
|
+
...pathItemConfig
|
|
225
|
+
} = context.oapi || {};
|
|
226
|
+
const route = pathOverride ?? convertExpressPath(context.path);
|
|
227
|
+
const pathItem = {
|
|
228
|
+
// -- defaults --
|
|
229
|
+
responses: {},
|
|
230
|
+
// has to be defined or else responses are not documented... ¯\_(ツ)_/¯
|
|
231
|
+
// -- group defaults --
|
|
232
|
+
...configs?.oapi || {},
|
|
233
|
+
// -- overrides --
|
|
234
|
+
...pathItemConfig || {},
|
|
235
|
+
parameters: [
|
|
236
|
+
// ...(contract.pathParameters || []),
|
|
237
|
+
...headerParameters || [],
|
|
238
|
+
...queryParameters || []
|
|
239
|
+
]
|
|
240
|
+
};
|
|
241
|
+
if (pathParameters?.length) {
|
|
242
|
+
pathItem.parameters.push(...pathParameters);
|
|
243
|
+
} else {
|
|
244
|
+
pathItem.parameters.push(...tryParsePathParameters(route));
|
|
245
|
+
}
|
|
246
|
+
swaggerDoc.paths[route] = {
|
|
247
|
+
[context.method]: pathItem
|
|
248
|
+
};
|
|
249
|
+
return router;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// src/env.ts
|
|
257
|
+
var import_dotenv = __toESM(require("dotenv"));
|
|
258
|
+
import_dotenv.default.config();
|
|
259
|
+
function isDev() {
|
|
260
|
+
return getEnvVar("ENV") === "dev";
|
|
261
|
+
}
|
|
262
|
+
function isProd() {
|
|
263
|
+
return getEnvVar("ENV") === "prod";
|
|
264
|
+
}
|
|
265
|
+
function getEnvVar(configName) {
|
|
266
|
+
const config = process.env[configName];
|
|
267
|
+
if (!config) {
|
|
268
|
+
throw new Error(`Missing config '${configName}'`);
|
|
269
|
+
}
|
|
270
|
+
return config;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/errors.ts
|
|
274
|
+
var ApiError = class extends Error {
|
|
275
|
+
code;
|
|
276
|
+
httpStatusCode;
|
|
277
|
+
data;
|
|
278
|
+
constructor(message, httpStatusCode, errorCode) {
|
|
279
|
+
super(message);
|
|
280
|
+
this.name = this.constructor.name;
|
|
281
|
+
this.code = errorCode;
|
|
282
|
+
this.httpStatusCode = httpStatusCode;
|
|
283
|
+
}
|
|
284
|
+
setData(data) {
|
|
285
|
+
this.data = data;
|
|
286
|
+
return this;
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
var NotFoundError = class extends ApiError {
|
|
290
|
+
constructor(message = "Resource not found") {
|
|
291
|
+
super(message, 404, "NOT_FOUND");
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
var DuplicateError = class extends ApiError {
|
|
295
|
+
constructor(message = "Duplicate entry") {
|
|
296
|
+
super(message, 409, "DUPLICATE_ENTRY");
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
var BadRequestError = class extends ApiError {
|
|
300
|
+
constructor(message = "Bad request") {
|
|
301
|
+
super(message, 400, "BAD_REQUEST");
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
var SchemaValidationError = class extends ApiError {
|
|
305
|
+
constructor(message = "Failed to validate Schema") {
|
|
306
|
+
super(message, 400, "SCHEMA_VALIDATION_ERROR");
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
var FileTooBigError = class extends ApiError {
|
|
310
|
+
constructor() {
|
|
311
|
+
super("File too big", 400, "FILE_TOO_BIG");
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
var InvalidFileTypeError = class extends ApiError {
|
|
315
|
+
constructor() {
|
|
316
|
+
super("Invalid file type", 400, "INVALID_FILE_TYPE");
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
var InvalidCredentialsError = class extends ApiError {
|
|
320
|
+
constructor() {
|
|
321
|
+
super("Invalid credentials", 401, "INVALID_CREDENTIALS");
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
var InternalError = class extends ApiError {
|
|
325
|
+
constructor() {
|
|
326
|
+
super("Internal error", 500, "INTERNAL_ERROR");
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
var TooManyRequestsError = class extends ApiError {
|
|
330
|
+
constructor(message = "Too many requests") {
|
|
331
|
+
super(message, 429, "TOO_MANY_REQUESTS");
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
var ForbiddenError = class extends ApiError {
|
|
335
|
+
constructor(message = "Action not allowed") {
|
|
336
|
+
super(message, 403, "FORBIDDEN");
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
var TokenExpiredError = class extends ApiError {
|
|
340
|
+
constructor(message = "Token Expired") {
|
|
341
|
+
super(message, 401, "TOKEN_EXPIRED");
|
|
342
|
+
}
|
|
343
|
+
};
|
|
344
|
+
var UserUnauthorizedError = class extends ApiError {
|
|
345
|
+
constructor(message = "User unauthorized") {
|
|
346
|
+
super(message, 401, "USER_UNAUTHORIZED");
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// src/response/ApiErrorResponse.ts
|
|
351
|
+
var ApiErrorResponse = class {
|
|
352
|
+
status = "error";
|
|
353
|
+
message;
|
|
354
|
+
errorCode;
|
|
355
|
+
errors;
|
|
356
|
+
constructor(message, errorCode, errors) {
|
|
357
|
+
this.message = message;
|
|
358
|
+
this.errorCode = errorCode;
|
|
359
|
+
this.errors = errors;
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
// src/common.ts
|
|
364
|
+
var import_path = __toESM(require("path"));
|
|
365
|
+
function parsePositiveInteger(v, defaultValue, max) {
|
|
366
|
+
const value = Number(v);
|
|
367
|
+
return Number.isInteger(value) && value > 0 && (!max || value <= max) ? value : defaultValue;
|
|
368
|
+
}
|
|
369
|
+
function parseIdOrFail(v) {
|
|
370
|
+
const value = Number(v);
|
|
371
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
372
|
+
throw new ApiError("Invalid Id", 400, "INVALID_ID");
|
|
373
|
+
}
|
|
374
|
+
return value;
|
|
375
|
+
}
|
|
376
|
+
function slugify(text) {
|
|
377
|
+
return text.toString().normalize("NFKD").toLowerCase().trim().replace(/[\s\n_+.]/g, "-").replace(/--+/g, "-");
|
|
378
|
+
}
|
|
379
|
+
function getTmpDir() {
|
|
380
|
+
return import_path.default.resolve("tmp");
|
|
381
|
+
}
|
|
382
|
+
function getTmpPath(...steps) {
|
|
383
|
+
return import_path.default.join(getTmpDir(), ...steps);
|
|
384
|
+
}
|
|
385
|
+
function parseDefaultPagination(query) {
|
|
386
|
+
const limit = parsePositiveInteger(query.limit, 50, 100);
|
|
387
|
+
const page = parsePositiveInteger(query.page, 1, 1e3);
|
|
388
|
+
const offset = (page - 1) * limit;
|
|
389
|
+
return { limit, offset };
|
|
390
|
+
}
|
|
391
|
+
function createReqSnapshot(req) {
|
|
392
|
+
return {
|
|
393
|
+
query: req.query,
|
|
394
|
+
path: req.path,
|
|
395
|
+
method: req.method,
|
|
396
|
+
userId: req?.user?.id
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/middleware/errorHandlerMiddleware.ts
|
|
401
|
+
var buildErrorHandlerMiddleware = (container) => {
|
|
402
|
+
const { logger, alertHandler } = container;
|
|
403
|
+
return {
|
|
404
|
+
getErrorHandlerMiddleware: (errorMapper) => {
|
|
405
|
+
return async (err, req, res, _next) => {
|
|
406
|
+
let finalError;
|
|
407
|
+
const customMappedError = errorMapper && errorMapper(err);
|
|
408
|
+
if (customMappedError) {
|
|
409
|
+
finalError = customMappedError;
|
|
410
|
+
logger.error("%s\n%o", finalError.message, finalError.data);
|
|
411
|
+
} else if (err.name === "SyntaxError" && err.type === "entity.parse.failed") {
|
|
412
|
+
logger.error("%s", err);
|
|
413
|
+
finalError = new BadRequestError("Invalid json format");
|
|
414
|
+
} else if (err instanceof ApiError) {
|
|
415
|
+
logger.error("%s", err);
|
|
416
|
+
finalError = err;
|
|
417
|
+
} else {
|
|
418
|
+
logger.error("Error: %s", err);
|
|
419
|
+
if (err.cause) {
|
|
420
|
+
logger.error("Cause: %s", err.cause);
|
|
421
|
+
}
|
|
422
|
+
if (alertHandler) {
|
|
423
|
+
alertHandler(err, createReqSnapshot(req));
|
|
424
|
+
}
|
|
425
|
+
finalError = new InternalError();
|
|
426
|
+
if (!isProd()) {
|
|
427
|
+
finalError.data = { name: err.name, message: err.message, stack: err.stack, cause: err.cause };
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
res.status(finalError.httpStatusCode).json(new ApiErrorResponse(finalError.message, finalError.code, finalError.data));
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
// src/middleware/notFoundMiddleware.ts
|
|
437
|
+
var notFoundMiddleware = (_req, res, _next) => {
|
|
438
|
+
res.status(404).send("\xAF\\_(\u30C4)_/\xAF").end();
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
// src/logger.ts
|
|
442
|
+
var import_winston = __toESM(require("winston"));
|
|
443
|
+
var import_winston_daily_rotate_file = __toESM(require("winston-daily-rotate-file"));
|
|
444
|
+
var loggerRegistry = {};
|
|
445
|
+
var defaultFormat = import_winston.default.format.combine(
|
|
446
|
+
import_winston.default.format.timestamp({ format: "DD/MM/YYYY HH:mm:ss" }),
|
|
447
|
+
import_winston.default.format.splat(),
|
|
448
|
+
// String interpolation splat for %d %s-style messages.
|
|
449
|
+
import_winston.default.format.errors({ stack: true }),
|
|
450
|
+
import_winston.default.format.printf(({ level, message, timestamp, stack }) => `${timestamp}[${level.toUpperCase()}]: ${stack || message}`)
|
|
451
|
+
);
|
|
452
|
+
var consoleTransport = new import_winston.default.transports.Console({
|
|
453
|
+
handleExceptions: true
|
|
454
|
+
});
|
|
455
|
+
var createFileLogger = (filename) => {
|
|
456
|
+
const logger = import_winston.default.createLogger({
|
|
457
|
+
format: defaultFormat,
|
|
458
|
+
transports: [
|
|
459
|
+
new import_winston_daily_rotate_file.default({
|
|
460
|
+
level: "debug",
|
|
461
|
+
filename: `./logs/${filename}-%DATE%.log`,
|
|
462
|
+
handleExceptions: true,
|
|
463
|
+
// exitOnError: false, // do not exit on handled exceptions
|
|
464
|
+
datePattern: "YYYY-MM-DD",
|
|
465
|
+
zippedArchive: true,
|
|
466
|
+
auditFile: "./logs/audit.json",
|
|
467
|
+
maxSize: "20m",
|
|
468
|
+
maxFiles: isProd() ? "30d" : "1d"
|
|
469
|
+
// format: defaultFormat,
|
|
470
|
+
// the nested 'format' field causes issues with logging errors;
|
|
471
|
+
// use the 'format' field on logger, instead of transport;
|
|
472
|
+
})
|
|
473
|
+
]
|
|
474
|
+
});
|
|
475
|
+
if (isDev()) {
|
|
476
|
+
logger.add(consoleTransport);
|
|
477
|
+
}
|
|
478
|
+
return logger;
|
|
479
|
+
};
|
|
480
|
+
var createLogger = import_winston.default.createLogger;
|
|
481
|
+
var getDefaultFileLogger = (name = "app") => {
|
|
482
|
+
if (!loggerRegistry[name]) {
|
|
483
|
+
loggerRegistry[name] = createFileLogger(name.toString());
|
|
484
|
+
}
|
|
485
|
+
return loggerRegistry[name];
|
|
486
|
+
};
|
|
487
|
+
var getDefaultConsoleLogger = (name = "console") => {
|
|
488
|
+
if (!loggerRegistry[name]) {
|
|
489
|
+
loggerRegistry[name] = import_winston.default.createLogger({
|
|
490
|
+
format: defaultFormat,
|
|
491
|
+
transports: [
|
|
492
|
+
consoleTransport
|
|
493
|
+
]
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
return loggerRegistry[name];
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
// src/response/ApiResponse.ts
|
|
500
|
+
var ApiResponse = class {
|
|
501
|
+
status = "ok";
|
|
502
|
+
result;
|
|
503
|
+
constructor(result) {
|
|
504
|
+
this.result = result;
|
|
505
|
+
}
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
// src/index.ts
|
|
509
|
+
function bootstrap(container) {
|
|
510
|
+
const swaggerDoc = {
|
|
511
|
+
openapi: "3.1.0",
|
|
512
|
+
// TODO
|
|
513
|
+
info: {},
|
|
514
|
+
paths: {},
|
|
515
|
+
components: {}
|
|
516
|
+
};
|
|
517
|
+
return {
|
|
518
|
+
...buildExpressive(container, swaggerDoc),
|
|
519
|
+
...buildSwaggerBuilder(swaggerDoc),
|
|
520
|
+
...buildErrorHandlerMiddleware(container),
|
|
521
|
+
notFoundMiddleware,
|
|
522
|
+
silently: (fn, reqSnapshot) => {
|
|
523
|
+
fn().catch((e) => {
|
|
524
|
+
if (container.alertHandler && e instanceof Error) {
|
|
525
|
+
container.alertHandler(e, reqSnapshot);
|
|
526
|
+
} else {
|
|
527
|
+
container.logger.error(e);
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
534
|
+
0 && (module.exports = {
|
|
535
|
+
ApiError,
|
|
536
|
+
ApiErrorResponse,
|
|
537
|
+
ApiResponse,
|
|
538
|
+
BadRequestError,
|
|
539
|
+
DuplicateError,
|
|
540
|
+
FileTooBigError,
|
|
541
|
+
ForbiddenError,
|
|
542
|
+
InternalError,
|
|
543
|
+
InvalidCredentialsError,
|
|
544
|
+
InvalidFileTypeError,
|
|
545
|
+
NotFoundError,
|
|
546
|
+
SWG,
|
|
547
|
+
SchemaValidationError,
|
|
548
|
+
TokenExpiredError,
|
|
549
|
+
TooManyRequestsError,
|
|
550
|
+
UserUnauthorizedError,
|
|
551
|
+
bootstrap,
|
|
552
|
+
createLogger,
|
|
553
|
+
createReqSnapshot,
|
|
554
|
+
getDefaultConsoleLogger,
|
|
555
|
+
getDefaultFileLogger,
|
|
556
|
+
getEnvVar,
|
|
557
|
+
getTmpDir,
|
|
558
|
+
getTmpPath,
|
|
559
|
+
isDev,
|
|
560
|
+
isProd,
|
|
561
|
+
parseDefaultPagination,
|
|
562
|
+
parseIdOrFail,
|
|
563
|
+
parsePositiveInteger,
|
|
564
|
+
slugify
|
|
565
|
+
});
|