@drunkcod/express-kit 0.0.8 → 0.0.9

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.
@@ -0,0 +1,39 @@
1
+ import type express from 'express';
2
+ type WithReturn<Type extends (...args: any) => any, R> = Type extends (...args: infer Args) => any ? (...args: Args) => R : never;
3
+ type IsEmptyObject<T extends Record<PropertyKey, unknown>> = [keyof T] extends [never] ? true : false;
4
+ type RequestHandler<T, Req extends express.Request<any> = express.Request<any>> = ((req: Req, res: express.Response<any>) => Promise<T>) | ((req: Req, res: express.Response<any>, next: express.NextFunction) => Promise<T>);
5
+ type RequestParams = [
6
+ express.Request<any>,
7
+ express.Response
8
+ ] | [express.Request<any>, express.Response, express.NextFunction];
9
+ type ErrorParams = [Error, express.Request<any>, express.Response, express.NextFunction];
10
+ type IsRequestHandler<T, P extends keyof T> = T[P] extends (...args: any) => any ? Parameters<T[P]> extends RequestParams ? P : never : never;
11
+ type IsErrorHandler<T, P extends keyof T> = T[P] extends (...args: any) => any ? Parameters<T[P]> extends ErrorParams ? P : never : never;
12
+ type ErrorHandler<T> = WithReturn<express.ErrorRequestHandler, Promise<T>>;
13
+ type HandlerFns<C> = {
14
+ [P in keyof C as IsRequestHandler<C, P>]: C[P];
15
+ };
16
+ type HandlerErrorFns<C> = {
17
+ [P in keyof C as IsErrorHandler<C, P>]: C[P];
18
+ };
19
+ type ControllerHandlerFns<C> = IsEmptyObject<HandlerFns<C>> extends true ? never : HandlerFns<C>;
20
+ type ControllerErrorFns<C> = IsEmptyObject<HandlerErrorFns<C>> extends true ? never : HandlerErrorFns<C>;
21
+ type ControllerFns<C> = ControllerHandlerFns<C> | ControllerErrorFns<C>;
22
+ type HandlerFn<Req, T> = Req extends [never] ? never : ((...args: [request: Req, response: express.Response]) => Promise<T>) | ((...args: [request: Req, response: express.Response, next: express.NextFunction]) => Promise<T>);
23
+ type ErrorHandlerFn<Req, T> = Req extends [never] ? never : ((...args: [errpr: Error, request: Req, response: express.Response, next: express.NextFunction]) => Promise<T>);
24
+ export declare const as: <T>(x: T) => T;
25
+ export declare function asyncHandler<T, Req extends express.Request = express.Request<any>>(fn: HandlerFn<Req, T>): RequestHandler<T>;
26
+ export declare function asyncHandler<T, Req extends express.Request = express.Request<any>>(fn: ErrorHandlerFn<Req, T>): ErrorHandler<T>;
27
+ export declare function boundAsyncHandler<C extends ControllerHandlerFns<C>, T>(x: C, m: keyof ControllerHandlerFns<C>): RequestHandler<unknown>;
28
+ export declare function boundAsyncHandler<C extends ControllerErrorFns<C>, T>(x: C, m: keyof ControllerErrorFns<C>): ErrorHandler<unknown>;
29
+ export declare class AsyncBinder<Controller extends ControllerFns<Controller>> {
30
+ private controller;
31
+ static for<Controller extends ControllerFns<Controller>>(controller: Controller): {
32
+ (m: keyof ControllerHandlerFns<Controller>): RequestHandler<unknown>;
33
+ (m: keyof ControllerErrorFns<Controller>): ErrorHandler<unknown>;
34
+ };
35
+ constructor(controller: Controller);
36
+ bind(m: keyof ControllerHandlerFns<Controller>): RequestHandler<unknown>;
37
+ bind(m: keyof ControllerErrorFns<Controller>): ErrorHandler<unknown>;
38
+ }
39
+ export {};
@@ -0,0 +1,37 @@
1
+ const safeResolve = (fn, ...args) => {
2
+ try {
3
+ return Promise.resolve(fn(...args));
4
+ }
5
+ catch (reason) {
6
+ return Promise.reject(reason);
7
+ }
8
+ };
9
+ export const as = (x) => x;
10
+ export function asyncHandler(fn) {
11
+ switch (fn.length) {
12
+ case 2:
13
+ case 3: return (req, res, next) => safeResolve(fn, req, res, next).catch(next);
14
+ case 4: return (error, req, res, next) => safeResolve(fn, error, req, res, next).catch(next);
15
+ default: return (...args) => {
16
+ const next = args.pop();
17
+ return safeResolve(fn, ...args, next).catch(next);
18
+ };
19
+ }
20
+ }
21
+ export function boundAsyncHandler(x, m) {
22
+ return asyncHandler(as(x[m]).bind(x));
23
+ }
24
+ export class AsyncBinder {
25
+ controller;
26
+ static for(controller) {
27
+ const self = new AsyncBinder(controller);
28
+ return self.bind.bind(self);
29
+ }
30
+ constructor(controller) {
31
+ this.controller = controller;
32
+ }
33
+ bind(m) {
34
+ const x = this.controller;
35
+ return asyncHandler(as(x[m]).bind(x));
36
+ }
37
+ }
package/lib/index.d.ts ADDED
@@ -0,0 +1,19 @@
1
+ import type express from 'express';
2
+ export * from '@drunkcod/express-async';
3
+ export * from './loggable.js';
4
+ type AsyncFn<T> = () => Promise<T>;
5
+ type ExpressServer = ReturnType<express.Application['listen']>;
6
+ export type ErrorHandler = (error: Error, request: express.Request, response: express.Response, next: express.NextFunction) => void;
7
+ export declare function onceAsync<T>(fn: AsyncFn<T>): AsyncFn<T>;
8
+ export declare function mergeCallsAsync<T>(fn: AsyncFn<T>): AsyncFn<T>;
9
+ interface Listener<T> {
10
+ listen(cb: () => void): T;
11
+ listen(port: number, cb: () => void): T;
12
+ }
13
+ export declare function listenAsync<T>(server: Listener<T>, options?: {
14
+ port?: number;
15
+ }): Promise<T>;
16
+ export declare function closeAsync(server: {
17
+ close: (cb: (error?: Error) => void) => void;
18
+ }): Promise<void>;
19
+ export declare function registerShutdown<Server extends ExpressServer = ExpressServer>(server: Server, shutdown?: () => Promise<void>): void;
package/lib/index.js ADDED
@@ -0,0 +1,59 @@
1
+ export * from '@drunkcod/express-async';
2
+ export * from './loggable.js';
3
+ export function onceAsync(fn) {
4
+ let p;
5
+ return () => {
6
+ if (p)
7
+ return p;
8
+ p = fn();
9
+ fn = () => p;
10
+ return p;
11
+ };
12
+ }
13
+ export function mergeCallsAsync(fn) {
14
+ let p;
15
+ return async () => {
16
+ if (!p)
17
+ p = fn();
18
+ try {
19
+ return await p;
20
+ }
21
+ finally {
22
+ p = null;
23
+ }
24
+ };
25
+ }
26
+ export function listenAsync(server, options) {
27
+ return new Promise((resolve, reject) => {
28
+ try {
29
+ if (options?.port) {
30
+ const r = server.listen(options.port, () => resolve(r));
31
+ }
32
+ else {
33
+ const r = server.listen(() => resolve(r));
34
+ }
35
+ }
36
+ catch (err) {
37
+ reject(err);
38
+ }
39
+ });
40
+ }
41
+ export function closeAsync(server) {
42
+ return new Promise((resolve, reject) => {
43
+ server.close((err) => {
44
+ if (err)
45
+ reject(err);
46
+ else
47
+ resolve();
48
+ });
49
+ });
50
+ }
51
+ export function registerShutdown(server, shutdown) {
52
+ const onShutdown = onceAsync(async () => {
53
+ await closeAsync(server);
54
+ if (shutdown)
55
+ await shutdown();
56
+ });
57
+ process.on('SIGINT', onShutdown);
58
+ process.on('SIGTERM', onShutdown);
59
+ }
@@ -0,0 +1,2 @@
1
+ export declare function at(message?: string): string;
2
+ export declare function asLoggableError(error: unknown): object;
@@ -0,0 +1,36 @@
1
+ import { hasOwn } from "@drunkcod/argis";
2
+ export function at(message) {
3
+ const old = Error.stackTraceLimit;
4
+ try {
5
+ const r = { stack: '' };
6
+ Error.stackTraceLimit = 1;
7
+ Error.captureStackTrace(r, at);
8
+ message = message ? `${message} ` : '';
9
+ return message + r.stack.substring(10);
10
+ }
11
+ finally {
12
+ Error.stackTraceLimit = old;
13
+ }
14
+ }
15
+ function asLoggableCause(cause) {
16
+ if (cause == null || typeof cause !== 'object')
17
+ return cause;
18
+ if (cause instanceof Error) {
19
+ const { message, stack, cause: innerCause, ...rest } = cause;
20
+ return innerCause
21
+ ? { message, stack, cause: asLoggableCause(innerCause), ...rest }
22
+ : { message, stack, ...rest };
23
+ }
24
+ if (hasOwn(cause, 'cause')) {
25
+ const { cause: innerCause, ...rest } = cause;
26
+ return { ...rest, cause: asLoggableCause(innerCause) };
27
+ }
28
+ return { ...cause };
29
+ }
30
+ export function asLoggableError(error) {
31
+ if (error instanceof Error)
32
+ return asLoggableCause(error);
33
+ const r = (error && typeof error === 'object') ? asLoggableCause(error) : { message: error };
34
+ Error.captureStackTrace(r, asLoggableError);
35
+ return Object.defineProperty(r, 'stack', { enumerable: true });
36
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@drunkcod/express-kit",
3
3
  "type": "module",
4
- "version": "0.0.8",
4
+ "version": "0.0.9",
5
5
  "description": "Express4 utility things",
6
6
  "main": "lib/index.js",
7
7
  "types": "lib/index.d.ts",
@@ -26,7 +26,7 @@
26
26
  ],
27
27
  "dependencies": {
28
28
  "@drunkcod/argis": "^0.0.5",
29
- "@drunkcod/express-async": "^0.0.7"
29
+ "@drunkcod/express-async": "^0.0.9"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@drunkcod/ts-jest-esm": "^0.0.1",