@xylabs/express 4.3.14
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/LICENSE +165 -0
- package/README.md +62 -0
- package/dist/node/AWS/getEnvFromAws.d.ts +2 -0
- package/dist/node/AWS/getEnvFromAws.d.ts.map +1 -0
- package/dist/node/AWS/index.d.ts +2 -0
- package/dist/node/AWS/index.d.ts.map +1 -0
- package/dist/node/Handler/StatusCodeHandlers/index.d.ts +2 -0
- package/dist/node/Handler/StatusCodeHandlers/index.d.ts.map +1 -0
- package/dist/node/Handler/StatusCodeHandlers/notImplemented.d.ts +3 -0
- package/dist/node/Handler/StatusCodeHandlers/notImplemented.d.ts.map +1 -0
- package/dist/node/Handler/asyncHandler.d.ts +11 -0
- package/dist/node/Handler/asyncHandler.d.ts.map +1 -0
- package/dist/node/Handler/errorToJsonHandler.d.ts +4 -0
- package/dist/node/Handler/errorToJsonHandler.d.ts.map +1 -0
- package/dist/node/Handler/index.d.ts +4 -0
- package/dist/node/Handler/index.d.ts.map +1 -0
- package/dist/node/HttpUtil/getHttpHeader.d.ts +12 -0
- package/dist/node/HttpUtil/getHttpHeader.d.ts.map +1 -0
- package/dist/node/HttpUtil/index.d.ts +2 -0
- package/dist/node/HttpUtil/index.d.ts.map +1 -0
- package/dist/node/Logger/LogFormats/LocalDev/index.d.ts +2 -0
- package/dist/node/Logger/LogFormats/LocalDev/index.d.ts.map +1 -0
- package/dist/node/Logger/LogFormats/LocalDev/logFormatLocalDev.d.ts +2 -0
- package/dist/node/Logger/LogFormats/LocalDev/logFormatLocalDev.d.ts.map +1 -0
- package/dist/node/Logger/LogFormats/Rollbar/index.d.ts +2 -0
- package/dist/node/Logger/LogFormats/Rollbar/index.d.ts.map +1 -0
- package/dist/node/Logger/LogFormats/Rollbar/logFormatRollbar.d.ts +2 -0
- package/dist/node/Logger/LogFormats/Rollbar/logFormatRollbar.d.ts.map +1 -0
- package/dist/node/Logger/LogFormats/Structured/index.d.ts +2 -0
- package/dist/node/Logger/LogFormats/Structured/index.d.ts.map +1 -0
- package/dist/node/Logger/LogFormats/Structured/logFormatStructured.d.ts +2 -0
- package/dist/node/Logger/LogFormats/Structured/logFormatStructured.d.ts.map +1 -0
- package/dist/node/Logger/LogFormats/index.d.ts +4 -0
- package/dist/node/Logger/LogFormats/index.d.ts.map +1 -0
- package/dist/node/Logger/Logger.d.ts +13 -0
- package/dist/node/Logger/Logger.d.ts.map +1 -0
- package/dist/node/Logger/LoggerMeta.d.ts +2 -0
- package/dist/node/Logger/LoggerMeta.d.ts.map +1 -0
- package/dist/node/Logger/LoggerOptions.d.ts +7 -0
- package/dist/node/Logger/LoggerOptions.d.ts.map +1 -0
- package/dist/node/Logger/LoggerVerbosity.d.ts +2 -0
- package/dist/node/Logger/LoggerVerbosity.d.ts.map +1 -0
- package/dist/node/Logger/Transports/Rollbar/RollbarTransport.d.ts +11 -0
- package/dist/node/Logger/Transports/Rollbar/RollbarTransport.d.ts.map +1 -0
- package/dist/node/Logger/Transports/Rollbar/canGetDefaultRollbarTransport.d.ts +4 -0
- package/dist/node/Logger/Transports/Rollbar/canGetDefaultRollbarTransport.d.ts.map +1 -0
- package/dist/node/Logger/Transports/Rollbar/getDefaultRollbarTransport.d.ts +5 -0
- package/dist/node/Logger/Transports/Rollbar/getDefaultRollbarTransport.d.ts.map +1 -0
- package/dist/node/Logger/Transports/Rollbar/index.d.ts +4 -0
- package/dist/node/Logger/Transports/Rollbar/index.d.ts.map +1 -0
- package/dist/node/Logger/Transports/index.d.ts +2 -0
- package/dist/node/Logger/Transports/index.d.ts.map +1 -0
- package/dist/node/Logger/WinstonVerbosity.d.ts +6 -0
- package/dist/node/Logger/WinstonVerbosity.d.ts.map +1 -0
- package/dist/node/Logger/WrappedWinstonLogger.d.ts +16 -0
- package/dist/node/Logger/WrappedWinstonLogger.d.ts.map +1 -0
- package/dist/node/Logger/getDefaultLogger.d.ts +3 -0
- package/dist/node/Logger/getDefaultLogger.d.ts.map +1 -0
- package/dist/node/Logger/getLogger.d.ts +4 -0
- package/dist/node/Logger/getLogger.d.ts.map +1 -0
- package/dist/node/Logger/index.d.ts +7 -0
- package/dist/node/Logger/index.d.ts.map +1 -0
- package/dist/node/Logger/toWinstonVerbosity.d.ts +4 -0
- package/dist/node/Logger/toWinstonVerbosity.d.ts.map +1 -0
- package/dist/node/Model/ExpressError.d.ts +4 -0
- package/dist/node/Model/ExpressError.d.ts.map +1 -0
- package/dist/node/Model/index.d.ts +2 -0
- package/dist/node/Model/index.d.ts.map +1 -0
- package/dist/node/Performance/Counters.d.ts +8 -0
- package/dist/node/Performance/Counters.d.ts.map +1 -0
- package/dist/node/Performance/Profiler.d.ts +5 -0
- package/dist/node/Performance/Profiler.d.ts.map +1 -0
- package/dist/node/Performance/index.d.ts +3 -0
- package/dist/node/Performance/index.d.ts.map +1 -0
- package/dist/node/Util/compactObject.d.ts +2 -0
- package/dist/node/Util/compactObject.d.ts.map +1 -0
- package/dist/node/Util/index.d.ts +3 -0
- package/dist/node/Util/index.d.ts.map +1 -0
- package/dist/node/Util/tryParse.d.ts +5 -0
- package/dist/node/Util/tryParse.d.ts.map +1 -0
- package/dist/node/index.d.ts +9 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.mjs +408 -0
- package/dist/node/index.mjs.map +1 -0
- package/dist/node/middleware/caseInsensitiveRouting/caseInsensitiveRouting.d.ts +14 -0
- package/dist/node/middleware/caseInsensitiveRouting/caseInsensitiveRouting.d.ts.map +1 -0
- package/dist/node/middleware/caseInsensitiveRouting/index.d.ts +2 -0
- package/dist/node/middleware/caseInsensitiveRouting/index.d.ts.map +1 -0
- package/dist/node/middleware/customPoweredByHeader/customPoweredByHeader.d.ts +15 -0
- package/dist/node/middleware/customPoweredByHeader/customPoweredByHeader.d.ts.map +1 -0
- package/dist/node/middleware/customPoweredByHeader/index.d.ts +2 -0
- package/dist/node/middleware/customPoweredByHeader/index.d.ts.map +1 -0
- package/dist/node/middleware/index.d.ts +6 -0
- package/dist/node/middleware/index.d.ts.map +1 -0
- package/dist/node/middleware/jsonBodyParser/index.d.ts +2 -0
- package/dist/node/middleware/jsonBodyParser/index.d.ts.map +1 -0
- package/dist/node/middleware/jsonBodyParser/jsonBodyParser.d.ts +33 -0
- package/dist/node/middleware/jsonBodyParser/jsonBodyParser.d.ts.map +1 -0
- package/dist/node/middleware/metrics/counters.d.ts +3 -0
- package/dist/node/middleware/metrics/counters.d.ts.map +1 -0
- package/dist/node/middleware/metrics/index.d.ts +3 -0
- package/dist/node/middleware/metrics/index.d.ts.map +1 -0
- package/dist/node/middleware/metrics/responseProfiler.d.ts +15 -0
- package/dist/node/middleware/metrics/responseProfiler.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/getResponseMetadata.d.ts +3 -0
- package/dist/node/middleware/standardResponses/getResponseMetadata.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/index.d.ts +5 -0
- package/dist/node/middleware/standardResponses/index.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/jsonApi/error.d.ts +51 -0
- package/dist/node/middleware/standardResponses/jsonApi/error.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/jsonApi/index.d.ts +6 -0
- package/dist/node/middleware/standardResponses/jsonApi/index.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/jsonApi/links.d.ts +7 -0
- package/dist/node/middleware/standardResponses/jsonApi/links.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/jsonApi/relationship.d.ts +38 -0
- package/dist/node/middleware/standardResponses/jsonApi/relationship.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/jsonApi/resourceIdentifier.d.ts +16 -0
- package/dist/node/middleware/standardResponses/jsonApi/resourceIdentifier.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/jsonApi/response.d.ts +44 -0
- package/dist/node/middleware/standardResponses/jsonApi/response.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/standardErrors.d.ts +4 -0
- package/dist/node/middleware/standardResponses/standardErrors.d.ts.map +1 -0
- package/dist/node/middleware/standardResponses/standardResponses.d.ts +18 -0
- package/dist/node/middleware/standardResponses/standardResponses.d.ts.map +1 -0
- package/package.json +101 -0
- package/src/AWS/getEnvFromAws.ts +24 -0
- package/src/AWS/index.ts +1 -0
- package/src/AWS/spec/getEnvFromAws.spec.ts +16 -0
- package/src/Handler/StatusCodeHandlers/index.ts +1 -0
- package/src/Handler/StatusCodeHandlers/notImplemented.ts +6 -0
- package/src/Handler/asyncHandler.ts +21 -0
- package/src/Handler/errorToJsonHandler.ts +14 -0
- package/src/Handler/index.ts +3 -0
- package/src/HttpUtil/getHttpHeader.ts +26 -0
- package/src/HttpUtil/index.ts +1 -0
- package/src/Logger/LogFormats/LocalDev/index.ts +1 -0
- package/src/Logger/LogFormats/LocalDev/logFormatLocalDev.ts +11 -0
- package/src/Logger/LogFormats/LocalDev/spec/logFormatLocalDev.spec.ts +13 -0
- package/src/Logger/LogFormats/Rollbar/index.ts +1 -0
- package/src/Logger/LogFormats/Rollbar/logFormatRollbar.ts +5 -0
- package/src/Logger/LogFormats/Rollbar/spec/logFormatRollbar.spec.ts +13 -0
- package/src/Logger/LogFormats/Structured/index.ts +1 -0
- package/src/Logger/LogFormats/Structured/logFormatStructured.ts +7 -0
- package/src/Logger/LogFormats/Structured/spec/logFormatStructured.spec.ts +13 -0
- package/src/Logger/LogFormats/index.ts +3 -0
- package/src/Logger/Logger.ts +15 -0
- package/src/Logger/LoggerMeta.ts +1 -0
- package/src/Logger/LoggerOptions.ts +7 -0
- package/src/Logger/LoggerVerbosity.ts +1 -0
- package/src/Logger/Transports/Rollbar/RollbarTransport.ts +22 -0
- package/src/Logger/Transports/Rollbar/canGetDefaultRollbarTransport.ts +3 -0
- package/src/Logger/Transports/Rollbar/getDefaultRollbarTransport.ts +10 -0
- package/src/Logger/Transports/Rollbar/index.ts +3 -0
- package/src/Logger/Transports/Rollbar/spec/RollbarTransport.spec.ts +30 -0
- package/src/Logger/Transports/Rollbar/spec/canGetDefaultRollbarTransport.spec.ts +18 -0
- package/src/Logger/Transports/Rollbar/spec/getDefaultRollbarTransport.spec.ts +16 -0
- package/src/Logger/Transports/index.ts +1 -0
- package/src/Logger/WinstonVerbosity.ts +5 -0
- package/src/Logger/WrappedWinstonLogger.ts +16 -0
- package/src/Logger/getDefaultLogger.ts +13 -0
- package/src/Logger/getLogger.ts +55 -0
- package/src/Logger/index.ts +6 -0
- package/src/Logger/spec/getDefaultLogger.spec.ts +14 -0
- package/src/Logger/spec/getLogger.spec.ts +25 -0
- package/src/Logger/spec/toWinstonVerbosity.spec.ts +17 -0
- package/src/Logger/toWinstonVerbosity.ts +6 -0
- package/src/Model/ExpressError.ts +3 -0
- package/src/Model/index.ts +1 -0
- package/src/Performance/Counters.ts +36 -0
- package/src/Performance/Profiler.ts +10 -0
- package/src/Performance/index.ts +2 -0
- package/src/Util/compactObject.ts +9 -0
- package/src/Util/index.ts +2 -0
- package/src/Util/tryParse.ts +21 -0
- package/src/index.ts +8 -0
- package/src/middleware/caseInsensitiveRouting/caseInsensitiveRouting.ts +21 -0
- package/src/middleware/caseInsensitiveRouting/index.ts +1 -0
- package/src/middleware/customPoweredByHeader/customPoweredByHeader.ts +29 -0
- package/src/middleware/customPoweredByHeader/index.ts +1 -0
- package/src/middleware/index.ts +5 -0
- package/src/middleware/jsonBodyParser/index.ts +1 -0
- package/src/middleware/jsonBodyParser/jsonBodyParser.ts +57 -0
- package/src/middleware/jsonBodyParser/spec/jsonBodyParser.spec.ts +26 -0
- package/src/middleware/metrics/counters.ts +25 -0
- package/src/middleware/metrics/index.ts +2 -0
- package/src/middleware/metrics/responseProfiler.ts +23 -0
- package/src/middleware/standardResponses/getResponseMetadata.ts +18 -0
- package/src/middleware/standardResponses/index.ts +4 -0
- package/src/middleware/standardResponses/jsonApi/README.md +4 -0
- package/src/middleware/standardResponses/jsonApi/error.ts +52 -0
- package/src/middleware/standardResponses/jsonApi/index.ts +5 -0
- package/src/middleware/standardResponses/jsonApi/links.ts +6 -0
- package/src/middleware/standardResponses/jsonApi/relationship.ts +43 -0
- package/src/middleware/standardResponses/jsonApi/resourceIdentifier.ts +15 -0
- package/src/middleware/standardResponses/jsonApi/response.ts +49 -0
- package/src/middleware/standardResponses/standardErrors.ts +25 -0
- package/src/middleware/standardResponses/standardResponses.ts +23 -0
- package/xy.config.ts +10 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import Rollbar from 'rollbar'
|
|
3
|
+
|
|
4
|
+
import { RollbarTransport } from './RollbarTransport.ts'
|
|
5
|
+
|
|
6
|
+
export const getDefaultRollbarTransport = (env: { [key: string]: string | undefined }): RollbarTransport => {
|
|
7
|
+
const accessToken = assertEx(env.ROLLBAR_ACCESS_TOKEN, 'Missing ROLLBAR_ACCESS_TOKEN ENV VAR')
|
|
8
|
+
const rollbar: Rollbar = new Rollbar({ accessToken })
|
|
9
|
+
return new RollbarTransport({}, rollbar)
|
|
10
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import Rollbar from 'rollbar'
|
|
4
|
+
import {
|
|
5
|
+
beforeEach,
|
|
6
|
+
describe, expect, it,
|
|
7
|
+
vi,
|
|
8
|
+
} from 'vitest'
|
|
9
|
+
import type { MockProxy } from 'vitest-mock-extended'
|
|
10
|
+
import { mock } from 'vitest-mock-extended'
|
|
11
|
+
|
|
12
|
+
import { RollbarTransport } from '../RollbarTransport.ts'
|
|
13
|
+
|
|
14
|
+
const accessToken = process.env.ROLLBAR_ACCESS_TOKEN
|
|
15
|
+
const unitTestSentinelLoggingString = 'error log from unit test'
|
|
16
|
+
|
|
17
|
+
describe('RollbarTransport', () => {
|
|
18
|
+
let rollbar: MockProxy<Rollbar> | Rollbar
|
|
19
|
+
let sut: RollbarTransport
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
rollbar = accessToken ? new Rollbar({ accessToken, environment: 'development' }) : mock<Rollbar>()
|
|
22
|
+
sut = new RollbarTransport({}, rollbar)
|
|
23
|
+
})
|
|
24
|
+
it('logs', () => {
|
|
25
|
+
expect(sut).toBeObject()
|
|
26
|
+
const nextMock = vi.fn()
|
|
27
|
+
sut.log({ message: unitTestSentinelLoggingString }, nextMock)
|
|
28
|
+
expect(nextMock).toHaveBeenCalledOnce()
|
|
29
|
+
})
|
|
30
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe, expect, it,
|
|
5
|
+
} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { canGetDefaultRollbarTransport } from '../canGetDefaultRollbarTransport.ts'
|
|
8
|
+
|
|
9
|
+
describe('canGetDefaultRollbarTransport', () => {
|
|
10
|
+
it('returns true if the transport could be created', () => {
|
|
11
|
+
const env = { ROLLBAR_ACCESS_TOKEN: 'something' }
|
|
12
|
+
expect(canGetDefaultRollbarTransport(env)).toBeTrue()
|
|
13
|
+
})
|
|
14
|
+
it('returns false if the transport could not be crated', () => {
|
|
15
|
+
const env = { ROLLBAR_ACCESS_TOKEN: undefined }
|
|
16
|
+
expect(canGetDefaultRollbarTransport(env)).toBeFalse()
|
|
17
|
+
})
|
|
18
|
+
})
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe, expect, it,
|
|
5
|
+
} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { getDefaultRollbarTransport } from '../getDefaultRollbarTransport.ts'
|
|
8
|
+
|
|
9
|
+
describe('getDefaultRollbarTransport', () => {
|
|
10
|
+
it('returns the transport', () => {
|
|
11
|
+
const env = { ROLLBAR_ACCESS_TOKEN: 'something' }
|
|
12
|
+
const transport = getDefaultRollbarTransport(env)
|
|
13
|
+
expect(transport).toBeObject()
|
|
14
|
+
expect(transport.log).toBeFunction()
|
|
15
|
+
})
|
|
16
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './Rollbar/index.ts'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Logger as Winston } from 'winston'
|
|
2
|
+
|
|
3
|
+
import type { LogFunction, Logger } from './Logger'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Wrap Winston logger methods to adapt to familiar
|
|
7
|
+
* console logging methods
|
|
8
|
+
*/
|
|
9
|
+
export class WrappedWinstonLogger implements Logger {
|
|
10
|
+
constructor(protected readonly winston: Winston) {}
|
|
11
|
+
debug: LogFunction = message => this.winston.debug(message)
|
|
12
|
+
error: LogFunction = message => this.winston.error(message)
|
|
13
|
+
info: LogFunction = message => this.winston.info(message)
|
|
14
|
+
log: LogFunction = message => this.winston.info(message)
|
|
15
|
+
warn: LogFunction = message => this.winston.warn(message)
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { getLogger } from './getLogger.ts'
|
|
2
|
+
import type { Logger } from './Logger.ts'
|
|
3
|
+
import type { WrappedWinstonLogger } from './WrappedWinstonLogger.ts'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Static instance to prevent multiple instances of the same logger
|
|
7
|
+
* with the same config
|
|
8
|
+
*/
|
|
9
|
+
let defaultLogger: WrappedWinstonLogger
|
|
10
|
+
export const getDefaultLogger = (): Logger => {
|
|
11
|
+
if (defaultLogger) return defaultLogger
|
|
12
|
+
return getLogger()
|
|
13
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createLogger, transports as winstonTransports } from 'winston'
|
|
2
|
+
import type TransportStream from 'winston-transport'
|
|
3
|
+
|
|
4
|
+
import { logFormatLocalDev, logFormatStructured } from './LogFormats/index.ts'
|
|
5
|
+
import type { Logger } from './Logger.ts'
|
|
6
|
+
import type { LoggerVerbosity } from './LoggerVerbosity.ts'
|
|
7
|
+
import { toWinstonVerbosity } from './toWinstonVerbosity.ts'
|
|
8
|
+
import { canGetDefaultRollbarTransport, getDefaultRollbarTransport } from './Transports/index.ts'
|
|
9
|
+
import type { WinstonVerbosity } from './WinstonVerbosity.ts'
|
|
10
|
+
import { WrappedWinstonLogger } from './WrappedWinstonLogger.ts'
|
|
11
|
+
|
|
12
|
+
const exitOnError = false
|
|
13
|
+
const handleRejections = true
|
|
14
|
+
|
|
15
|
+
const { Console } = winstonTransports
|
|
16
|
+
const consoleTransport = new Console()
|
|
17
|
+
const format = process.env.NODE_ENV === 'development' ? logFormatLocalDev : logFormatStructured
|
|
18
|
+
const transports: TransportStream[] = [consoleTransport]
|
|
19
|
+
if (canGetDefaultRollbarTransport(process.env)) {
|
|
20
|
+
try {
|
|
21
|
+
const rollbarTransport = getDefaultRollbarTransport(process.env)
|
|
22
|
+
transports.push(rollbarTransport)
|
|
23
|
+
} catch {
|
|
24
|
+
// NOTE: No error here, just gracefully adding logger if ENV VARs
|
|
25
|
+
// were preset
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const loggers: Record<WinstonVerbosity, Logger | undefined> = {
|
|
30
|
+
debug: undefined,
|
|
31
|
+
error: undefined,
|
|
32
|
+
http: undefined,
|
|
33
|
+
info: undefined,
|
|
34
|
+
silly: undefined,
|
|
35
|
+
verbose: undefined,
|
|
36
|
+
warn: undefined,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const getLogger = (minVerbosity: LoggerVerbosity = 'info'): Logger => {
|
|
40
|
+
const level = toWinstonVerbosity(minVerbosity)
|
|
41
|
+
const existing = loggers[level]
|
|
42
|
+
if (existing) return existing
|
|
43
|
+
const logger = new WrappedWinstonLogger(
|
|
44
|
+
createLogger({
|
|
45
|
+
exitOnError,
|
|
46
|
+
format,
|
|
47
|
+
handleRejections,
|
|
48
|
+
level,
|
|
49
|
+
rejectionHandlers: transports,
|
|
50
|
+
transports,
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
loggers[level] = logger
|
|
54
|
+
return logger
|
|
55
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe, expect, it,
|
|
5
|
+
} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { getDefaultLogger } from '../getDefaultLogger.ts'
|
|
8
|
+
|
|
9
|
+
describe('getDefaultLogger', () => {
|
|
10
|
+
it('provides a default logger', () => {
|
|
11
|
+
const logger = getDefaultLogger()
|
|
12
|
+
expect(logger).toBeObject()
|
|
13
|
+
})
|
|
14
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
+
import { mockDeep } from 'vitest-mock-extended'
|
|
3
|
+
globalThis.console = mockDeep<Console>()
|
|
4
|
+
import '@xylabs/vitest-extended'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
describe, expect, it,
|
|
8
|
+
} from 'vitest'
|
|
9
|
+
import type { Logger } from 'winston'
|
|
10
|
+
|
|
11
|
+
import { getLogger } from '../getLogger.ts'
|
|
12
|
+
|
|
13
|
+
type LoggerKey = keyof Logger
|
|
14
|
+
const loggerKeys: LoggerKey[] = ['error', 'warn', 'log', 'info', 'debug']
|
|
15
|
+
|
|
16
|
+
describe('getLogger', () => {
|
|
17
|
+
describe('verbosity', () => {
|
|
18
|
+
it.each(loggerKeys)('logs log with %s verbosity', (verbosity: LoggerKey) => {
|
|
19
|
+
const logger = getLogger('all')
|
|
20
|
+
const logMethod = (logger as any)[verbosity]
|
|
21
|
+
expect(logMethod).toBeFunction()
|
|
22
|
+
logMethod(`${new String(verbosity)} log from unit test`)
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe, expect, it,
|
|
5
|
+
} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import type { LoggerVerbosity } from '../LoggerVerbosity.ts'
|
|
8
|
+
import { toWinstonVerbosity } from '../toWinstonVerbosity.ts'
|
|
9
|
+
|
|
10
|
+
const loggerKeys: LoggerVerbosity[] = ['error', 'warn', 'info', 'debug', 'all']
|
|
11
|
+
|
|
12
|
+
describe('toWinstonVerbosity', () => {
|
|
13
|
+
it.each(loggerKeys)('provides a default logger', (verbosity) => {
|
|
14
|
+
const actual = toWinstonVerbosity(verbosity)
|
|
15
|
+
expect(actual).toBeString()
|
|
16
|
+
})
|
|
17
|
+
})
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LoggerVerbosity } from './LoggerVerbosity.ts'
|
|
2
|
+
import type { WinstonVerbosity } from './WinstonVerbosity.ts'
|
|
3
|
+
|
|
4
|
+
export const toWinstonVerbosity = (loggerVerbosity: LoggerVerbosity): WinstonVerbosity => {
|
|
5
|
+
return loggerVerbosity === 'all' ? 'silly' : loggerVerbosity
|
|
6
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ExpressError.ts'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export class Counters {
|
|
2
|
+
static counters: Record<string, number> = {}
|
|
3
|
+
|
|
4
|
+
static inc(name: string, count = 1) {
|
|
5
|
+
this.catchError(name, (name: string) => {
|
|
6
|
+
this.counters[name] = (this.counters[name] ?? 0) + count
|
|
7
|
+
})
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
static max(name: string, count: number) {
|
|
11
|
+
this.catchError(name, (name: string) => {
|
|
12
|
+
const currentValue = this.counters[name]
|
|
13
|
+
if (currentValue === undefined || count > currentValue) {
|
|
14
|
+
this.counters[name] = count
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
static min(name: string, count: number) {
|
|
20
|
+
this.catchError(name, (name: string) => {
|
|
21
|
+
const currentValue = this.counters[name]
|
|
22
|
+
if (currentValue === undefined || count < currentValue) {
|
|
23
|
+
this.counters[name] = count
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
private static catchError = (name: string, func: (name: string) => void) => {
|
|
29
|
+
try {
|
|
30
|
+
func(name)
|
|
31
|
+
} catch {
|
|
32
|
+
this.counters[name] = 0
|
|
33
|
+
this.inc('CountersErrors')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type ParseFunc<T = number> = (value: string) => T
|
|
2
|
+
|
|
3
|
+
export const tryParse = <T = number>(func: ParseFunc<T>, value?: string) => {
|
|
4
|
+
try {
|
|
5
|
+
const result = value ? func(value) : null
|
|
6
|
+
if (!Number.isNaN(result) && result !== null) {
|
|
7
|
+
return result
|
|
8
|
+
}
|
|
9
|
+
} catch {
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
return
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const tryParseFloat = (value?: string) => {
|
|
16
|
+
return tryParse(Number.parseFloat, value)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const tryParseInt = (value?: string) => {
|
|
20
|
+
return tryParse(Number.parseInt, value)
|
|
21
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './AWS/index.ts'
|
|
2
|
+
export * from './Handler/index.ts'
|
|
3
|
+
export * from './HttpUtil/index.ts'
|
|
4
|
+
export * from './Logger/index.ts'
|
|
5
|
+
export * from './middleware/index.ts'
|
|
6
|
+
export * from './Model/index.ts'
|
|
7
|
+
export * from './Performance/index.ts'
|
|
8
|
+
export * from './Util/index.ts'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Express } from 'express'
|
|
2
|
+
|
|
3
|
+
const setting = 'case sensitive routing'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enable case sensitivity. When enabled, "/Foo" and "/foo" are different
|
|
7
|
+
* routes. When disabled, "/Foo" and "/foo" are treated the same.
|
|
8
|
+
* @param app The Express app to disable the header on.
|
|
9
|
+
*/
|
|
10
|
+
export const enableCaseSensitiveRouting = (app: Express) => {
|
|
11
|
+
app.enable(setting)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Disable case sensitivity. When enabled, "/Foo" and "/foo" are different
|
|
16
|
+
* routes. When disabled, "/Foo" and "/foo" are treated the same.
|
|
17
|
+
* @param app The Express app to disable the header on.
|
|
18
|
+
*/
|
|
19
|
+
export const disableCaseSensitiveRouting = (app: Express) => {
|
|
20
|
+
app.disable(setting)
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './caseInsensitiveRouting.ts'
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Express, NextFunction, Request, Response,
|
|
3
|
+
} from 'express'
|
|
4
|
+
|
|
5
|
+
const header = 'X-Powered-By'
|
|
6
|
+
const setting = 'x-powered-by'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* By default Express appends the `X-Powered-By: Express` header to
|
|
10
|
+
* all responses. Calling this method enables that behavior.
|
|
11
|
+
* @param app The Express app to disable the header on.
|
|
12
|
+
*/
|
|
13
|
+
export const enableExpressDefaultPoweredByHeader = (app: Express) => {
|
|
14
|
+
app.enable(setting)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* By default Express appends the `X-Powered-By: Express` header to
|
|
19
|
+
* all responses. Calling this method disables that behavior.
|
|
20
|
+
* @param app The Express app to disable the header on.
|
|
21
|
+
*/
|
|
22
|
+
export const disableExpressDefaultPoweredByHeader = (app: Express) => {
|
|
23
|
+
app.disable(setting)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const customPoweredByHeader = (req: Request, res: Response, next: NextFunction) => {
|
|
27
|
+
res.setHeader(header, 'XYO')
|
|
28
|
+
next()
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './customPoweredByHeader.ts'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './jsonBodyParser.ts'
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OptionsJson } from 'body-parser'
|
|
2
|
+
import bodyParser from 'body-parser'
|
|
3
|
+
import type { NextHandleFunction } from 'connect'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The default maximum request body size for the JSON Body Parser
|
|
7
|
+
*/
|
|
8
|
+
export const DefaultJsonBodyParserOptionsLimit = '100kb'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The default MIME types for the JSON Body Parser
|
|
12
|
+
*/
|
|
13
|
+
export const DefaultJsonBodyParserOptionsTypes = ['application/json', 'text/json']
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The default options for the JSON Body Parser
|
|
17
|
+
*/
|
|
18
|
+
export const DefaultJsonBodyParserOptions: OptionsJson = {
|
|
19
|
+
limit: DefaultJsonBodyParserOptionsLimit,
|
|
20
|
+
type: DefaultJsonBodyParserOptionsTypes,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Gets the default JSON Body Parser options merged with the supplied options
|
|
25
|
+
* with the supplied options taking precedence
|
|
26
|
+
* @param options The options to override the default JSON Body Parser options with
|
|
27
|
+
* @returns The combined JSON Body Parser options with the supplied values taking
|
|
28
|
+
* precedence over the default
|
|
29
|
+
*/
|
|
30
|
+
export const getJsonBodyParserOptions = (options?: Partial<OptionsJson>): OptionsJson => {
|
|
31
|
+
return options ? { ...DefaultJsonBodyParserOptions, ...options } : DefaultJsonBodyParserOptions
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get a JSON Body Parser connect middleware handler
|
|
36
|
+
* @param options The options for the JSON Body Parser
|
|
37
|
+
* @returns A middleware function that parses JSON bodies
|
|
38
|
+
*/
|
|
39
|
+
export const getJsonBodyParser = (options: OptionsJson = DefaultJsonBodyParserOptions): NextHandleFunction => {
|
|
40
|
+
// Create closed instance of bodyParser to prevent instantiation of new instance on every request
|
|
41
|
+
const parser = bodyParser.json(options)
|
|
42
|
+
|
|
43
|
+
return (req, res, next) => {
|
|
44
|
+
// If we do not trap this error, then it dumps too much to log, usually happens if request aborted
|
|
45
|
+
try {
|
|
46
|
+
parser(req, res, next)
|
|
47
|
+
} catch (ex) {
|
|
48
|
+
const error = ex as Error
|
|
49
|
+
console.log(`bodyParser failed [${error?.name}]: ${error?.message}`)
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A JSON Body Parser middleware handler initialized with the default options
|
|
56
|
+
*/
|
|
57
|
+
export const jsonBodyParser = getJsonBodyParser()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import '@xylabs/vitest-extended'
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
describe, expect, it,
|
|
5
|
+
} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { DefaultJsonBodyParserOptions, getJsonBodyParserOptions } from '../jsonBodyParser.ts'
|
|
8
|
+
|
|
9
|
+
describe('jsonBodyParser', () => {
|
|
10
|
+
describe('getJsonBodyParserOptions', () => {
|
|
11
|
+
it('returns default options if none supplied', () => {
|
|
12
|
+
// Act
|
|
13
|
+
const result = getJsonBodyParserOptions()
|
|
14
|
+
// Assert
|
|
15
|
+
expect(result).toEqual(DefaultJsonBodyParserOptions)
|
|
16
|
+
})
|
|
17
|
+
it('returns merged options if options supplied', () => {
|
|
18
|
+
// Arrange
|
|
19
|
+
const options = { limit: '1mb' }
|
|
20
|
+
// Act
|
|
21
|
+
const result = getJsonBodyParserOptions(options)
|
|
22
|
+
// Assert
|
|
23
|
+
expect(result).toEqual({ ...DefaultJsonBodyParserOptions, ...options })
|
|
24
|
+
})
|
|
25
|
+
})
|
|
26
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Application, NextFunction, Request, Response,
|
|
3
|
+
} from 'express'
|
|
4
|
+
|
|
5
|
+
import { Counters } from '../../Performance/index.ts'
|
|
6
|
+
|
|
7
|
+
export const useRequestCounters = (app: Application): void => {
|
|
8
|
+
// Configure Global counters
|
|
9
|
+
app.use((req: Request, res: Response, next: NextFunction) => {
|
|
10
|
+
Counters.inc(req.path)
|
|
11
|
+
Counters.inc('_calls')
|
|
12
|
+
next()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
app.get('/stats', (req: Request, res: Response, next: NextFunction) => {
|
|
16
|
+
/* #swagger.tags = ['Metrics'] */
|
|
17
|
+
/* #swagger.summary = 'Get the counters for single instance of diviner' */
|
|
18
|
+
res.json({
|
|
19
|
+
alive: true,
|
|
20
|
+
avgTime: `${((Counters.counters['_totalTime'] ?? 0) / (Counters.counters['_calls'] ?? 1)).toFixed(2)}ms`,
|
|
21
|
+
counters: Counters.counters,
|
|
22
|
+
})
|
|
23
|
+
next()
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
NextFunction, Request, Response,
|
|
3
|
+
} from 'express'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Connect middleware to enable profiling of response lifecycle timing. To effectively profile
|
|
7
|
+
* the response timing, this middleware needs to be called first when initializing your Express
|
|
8
|
+
* App
|
|
9
|
+
* @example
|
|
10
|
+
* const app = express()
|
|
11
|
+
* app.use(responseProfiler)
|
|
12
|
+
* // other initialization ...
|
|
13
|
+
* @param _req The request
|
|
14
|
+
* @param res The response
|
|
15
|
+
* @param next The next function
|
|
16
|
+
*/
|
|
17
|
+
export const responseProfiler = (_req: Request, res: Response, next: NextFunction) => {
|
|
18
|
+
if (!res.locals?.meta) {
|
|
19
|
+
res.locals.meta = {}
|
|
20
|
+
}
|
|
21
|
+
res.locals.meta.profile = { startTime: Date.now() }
|
|
22
|
+
next()
|
|
23
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Response } from 'express'
|
|
2
|
+
|
|
3
|
+
export const getResponseMetadata = (res: Response): Record<string, unknown> => {
|
|
4
|
+
const meta: Record<string, unknown> = res.locals?.meta || {}
|
|
5
|
+
// NOTE: We should do this somewhere else to better separate concerns
|
|
6
|
+
const profile = res.locals.meta?.profile
|
|
7
|
+
if (profile) {
|
|
8
|
+
const startTime = profile?.startTime
|
|
9
|
+
if (startTime) {
|
|
10
|
+
const endTime = Date.now()
|
|
11
|
+
const duration = endTime - startTime
|
|
12
|
+
res.locals.meta.profile = {
|
|
13
|
+
duration, endTime, startTime,
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return meta
|
|
18
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { ApiLinks } from './links.ts'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* An object containing references to the source of the error
|
|
5
|
+
*/
|
|
6
|
+
export interface Source {
|
|
7
|
+
/**
|
|
8
|
+
* A string indicating which URI query parameter caused the error.
|
|
9
|
+
*/
|
|
10
|
+
parameter?: string
|
|
11
|
+
/**
|
|
12
|
+
* A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. "/data" for a primary data object,
|
|
13
|
+
* or "/data/attributes/title" for a specific attribute].
|
|
14
|
+
*/
|
|
15
|
+
pointer?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ApiError {
|
|
19
|
+
/**
|
|
20
|
+
* An application-specific error code, expressed as a string value.
|
|
21
|
+
*/
|
|
22
|
+
code?: string
|
|
23
|
+
/**
|
|
24
|
+
* A human-readable explanation specific to this occurrence of the problem. Like title, this field's value can be localized.
|
|
25
|
+
*/
|
|
26
|
+
detail?: string
|
|
27
|
+
/**
|
|
28
|
+
* A unique identifier for this particular occurrence of the problem.
|
|
29
|
+
*/
|
|
30
|
+
id?: string
|
|
31
|
+
/**
|
|
32
|
+
* A links object containing the following members:
|
|
33
|
+
* about: a link that leads to further details about this particular occurrence of the problem
|
|
34
|
+
*/
|
|
35
|
+
links?: ApiLinks
|
|
36
|
+
/**
|
|
37
|
+
* A meta object containing non-standard meta-information about the error.
|
|
38
|
+
*/
|
|
39
|
+
meta?: Record<string, unknown>
|
|
40
|
+
/**
|
|
41
|
+
* An object containing references to the source of the error, optionally including any of the following members:
|
|
42
|
+
*/
|
|
43
|
+
source?: Source
|
|
44
|
+
/**
|
|
45
|
+
* The HTTP status code applicable to this problem, expressed as a string value.
|
|
46
|
+
*/
|
|
47
|
+
status?: string
|
|
48
|
+
/**
|
|
49
|
+
* A short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization.
|
|
50
|
+
*/
|
|
51
|
+
title?: string
|
|
52
|
+
}
|