@naturalcycles/js-lib 15.90.0 → 15.91.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/asyncManager.js +3 -2
- package/dist/decorators/asyncMemo.decorator.d.ts +1 -1
- package/dist/decorators/asyncMemo.decorator.js +4 -3
- package/dist/decorators/logMethod.decorator.d.ts +1 -1
- package/dist/decorators/logMethod.decorator.js +8 -3
- package/dist/decorators/memo.decorator.d.ts +1 -1
- package/dist/decorators/memo.decorator.js +3 -2
- package/dist/decorators/memoSimple.decorator.js +8 -1
- package/dist/decorators/memoized.js +3 -2
- package/dist/decorators/memoizedAsync.js +4 -3
- package/dist/error/tryCatch.d.ts +1 -1
- package/dist/error/tryCatch.js +3 -2
- package/dist/http/fetcher.js +18 -17
- package/dist/http/fetcher.model.d.ts +1 -1
- package/dist/log/commonLogger.d.ts +35 -34
- package/dist/log/commonLogger.js +51 -96
- package/dist/promise/pGradualQueue.d.ts +1 -1
- package/dist/promise/pMap.d.ts +2 -2
- package/dist/promise/pMap.js +4 -3
- package/dist/promise/pQueue.d.ts +1 -1
- package/dist/promise/pRetry.d.ts +1 -1
- package/dist/promise/pRetry.js +3 -2
- package/package.json +2 -2
- package/src/asyncManager.ts +3 -2
- package/src/decorators/asyncMemo.decorator.ts +5 -4
- package/src/decorators/logMethod.decorator.ts +9 -4
- package/src/decorators/memo.decorator.ts +4 -3
- package/src/decorators/memoSimple.decorator.ts +2 -1
- package/src/decorators/memoized.ts +3 -2
- package/src/decorators/memoizedAsync.ts +4 -3
- package/src/error/tryCatch.ts +4 -3
- package/src/http/fetcher.model.ts +1 -1
- package/src/http/fetcher.ts +9 -10
- package/src/log/commonLogger.ts +78 -138
- package/src/promise/pGradualQueue.ts +1 -1
- package/src/promise/pMap.ts +10 -5
- package/src/promise/pQueue.ts +1 -1
- package/src/promise/pRetry.ts +4 -3
package/src/log/commonLogger.ts
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { _isPlainObject } from '../is.util.js'
|
|
2
|
-
import type { AnyObject, MutateOptions } from '../types.js'
|
|
3
|
-
|
|
4
1
|
// copy-pasted to avoid weird circular dependency
|
|
5
2
|
const _noop = (..._args: any[]): undefined => undefined
|
|
6
3
|
|
|
@@ -23,23 +20,37 @@ export type CommonLogLevel = 'debug' | 'log' | 'warn' | 'error'
|
|
|
23
20
|
export const commonLogLevelNumber: Record<CommonLogLevel, number> = {
|
|
24
21
|
debug: 10,
|
|
25
22
|
log: 20,
|
|
26
|
-
warn:
|
|
27
|
-
error:
|
|
23
|
+
warn: 30,
|
|
24
|
+
error: 40,
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
/**
|
|
31
|
-
*
|
|
32
|
-
* It is expected that logged arguments are separated by "space", like console.log does.
|
|
33
|
-
*
|
|
34
|
-
* @experimental
|
|
28
|
+
* Structured log data: a message, an error, and any other fields.
|
|
35
29
|
*/
|
|
36
|
-
export
|
|
37
|
-
|
|
30
|
+
export interface LogData {
|
|
31
|
+
msg?: string
|
|
32
|
+
err?: unknown
|
|
33
|
+
[field: string]: unknown
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* What a log call accepts: a string is `{ msg }`, an Error is `{ err }`.
|
|
38
|
+
* A non-Error error value (e.g an `unknown` from a catch) is passed as `{ err }`.
|
|
39
|
+
*/
|
|
40
|
+
export type LogInput = string | Error | LogData
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* What a sink receives: the logger's context, the log data and the level.
|
|
44
|
+
*/
|
|
45
|
+
export interface LogEntry extends LogData {
|
|
46
|
+
level: CommonLogLevel
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type LogSink = (entry: LogEntry) => void
|
|
50
|
+
|
|
51
|
+
export type CommonLogFunction = (input: LogInput) => void
|
|
38
52
|
|
|
39
53
|
/**
|
|
40
|
-
* Interface is inspired/compatible with `console.*`
|
|
41
|
-
* So, `console` is a valid CommonLogger implementation as-is.
|
|
42
|
-
*
|
|
43
54
|
* @experimental
|
|
44
55
|
*/
|
|
45
56
|
export interface CommonLogger {
|
|
@@ -47,161 +58,90 @@ export interface CommonLogger {
|
|
|
47
58
|
log: CommonLogFunction
|
|
48
59
|
warn: CommonLogFunction
|
|
49
60
|
error: CommonLogFunction
|
|
61
|
+
/**
|
|
62
|
+
* Creates a logger that adds `context` to every entry.
|
|
63
|
+
*/
|
|
64
|
+
child: (context: LogData) => CommonLogger
|
|
50
65
|
}
|
|
51
66
|
|
|
52
67
|
/**
|
|
53
|
-
*
|
|
68
|
+
* Creates a CommonLogger that writes every entry to `sink`.
|
|
69
|
+
* `context` is called on every log call, so it can return values that change
|
|
70
|
+
* after the logger is created (e.g a request context whose user is resolved later).
|
|
71
|
+
*/
|
|
72
|
+
export function commonLoggerCreate(sink: LogSink, context?: () => LogData): CommonLogger {
|
|
73
|
+
const emit = (level: CommonLogLevel, input: LogInput): void =>
|
|
74
|
+
sink({ ...context?.(), ...toLogData(input), level })
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
debug: input => emit('debug', input),
|
|
78
|
+
log: input => emit('log', input),
|
|
79
|
+
warn: input => emit('warn', input),
|
|
80
|
+
error: input => emit('error', input),
|
|
81
|
+
child: childContext => commonLoggerCreate(sink, () => ({ ...context?.(), ...childContext })),
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function toLogData(input: LogInput): LogData {
|
|
86
|
+
if (typeof input === 'string') return { msg: input }
|
|
87
|
+
if (input instanceof Error) return { err: input }
|
|
88
|
+
return input
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* CommonLogger that logs to `console`.
|
|
93
|
+
*/
|
|
94
|
+
export const consoleLogger: CommonLogger = commonLoggerCreate(({ level, msg, err, ...fields }) => {
|
|
95
|
+
const args: unknown[] = []
|
|
96
|
+
if (msg !== undefined) args.push(msg)
|
|
97
|
+
if (err !== undefined) args.push(err)
|
|
98
|
+
if (Object.keys(fields).length) args.push(fields)
|
|
99
|
+
console[level](...args)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* CommonLogger that does nothing (noop).
|
|
54
104
|
*/
|
|
55
105
|
export const commonLoggerNoop: CommonLogger = {
|
|
56
106
|
debug: _noop,
|
|
57
107
|
log: _noop,
|
|
58
108
|
warn: _noop,
|
|
59
109
|
error: _noop,
|
|
110
|
+
child: () => commonLoggerNoop,
|
|
60
111
|
}
|
|
61
112
|
|
|
62
113
|
/**
|
|
63
|
-
* Creates a
|
|
114
|
+
* Creates a CommonLogger that is "limited" to the specified CommonLogLevel.
|
|
64
115
|
*/
|
|
65
116
|
export function createCommonLoggerAtLevel(
|
|
66
|
-
logger: CommonLogger =
|
|
117
|
+
logger: CommonLogger = consoleLogger,
|
|
67
118
|
minLevel: CommonLogLevel = 'log',
|
|
68
|
-
opt: MutateOptions = {},
|
|
69
119
|
): CommonLogger {
|
|
70
120
|
const level = commonLogLevelNumber[minLevel]
|
|
71
|
-
if (opt.mutate) {
|
|
72
|
-
if (level > commonLogLevelNumber['debug']) {
|
|
73
|
-
logger.debug = _noop
|
|
74
|
-
if (level > commonLogLevelNumber['log']) {
|
|
75
|
-
logger.log = _noop
|
|
76
|
-
if (level > commonLogLevelNumber['warn']) {
|
|
77
|
-
logger.warn = _noop
|
|
78
|
-
if (level > commonLogLevelNumber['error']) {
|
|
79
|
-
logger.error = _noop
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return logger
|
|
85
|
-
}
|
|
86
121
|
|
|
87
122
|
if (level <= commonLogLevelNumber['debug']) {
|
|
88
123
|
// All levels are kept
|
|
89
124
|
return logger
|
|
90
125
|
}
|
|
91
126
|
|
|
92
|
-
if (level > commonLogLevelNumber['error']) {
|
|
93
|
-
// "Log nothing" logger
|
|
94
|
-
return commonLoggerNoop
|
|
95
|
-
}
|
|
96
|
-
|
|
97
127
|
return {
|
|
98
128
|
debug: _noop, // otherwise it is "log everything" logger (same logger as input)
|
|
99
|
-
log: level <= commonLogLevelNumber['log'] ? logger.log
|
|
100
|
-
warn: level <= commonLogLevelNumber['warn'] ? logger.warn
|
|
101
|
-
error: logger.error
|
|
129
|
+
log: level <= commonLogLevelNumber['log'] ? input => logger.log(input) : _noop,
|
|
130
|
+
warn: level <= commonLogLevelNumber['warn'] ? input => logger.warn(input) : _noop,
|
|
131
|
+
error: input => logger.error(input),
|
|
132
|
+
child: context => createCommonLoggerAtLevel(logger.child(context), minLevel),
|
|
102
133
|
}
|
|
103
134
|
}
|
|
104
135
|
|
|
105
136
|
/**
|
|
106
|
-
* Creates a "proxy" CommonLogger that pipes log
|
|
137
|
+
* Creates a "proxy" CommonLogger that pipes log entries to all provided sub-loggers.
|
|
107
138
|
*/
|
|
108
139
|
export function commonLoggerPipe(loggers: CommonLogger[]): CommonLogger {
|
|
109
140
|
return {
|
|
110
|
-
debug:
|
|
111
|
-
log:
|
|
112
|
-
warn:
|
|
113
|
-
error:
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Creates a "child" CommonLogger with prefix (one or multiple).
|
|
119
|
-
*/
|
|
120
|
-
export function commonLoggerPrefix(logger: CommonLogger, ...prefixes: any[]): CommonLogger {
|
|
121
|
-
return {
|
|
122
|
-
debug: (...args) => logger.debug(...prefixes, ...args),
|
|
123
|
-
log: (...args) => logger.log(...prefixes, ...args),
|
|
124
|
-
warn: (...args) => logger.warn(...prefixes, ...args),
|
|
125
|
-
error: (...args) => logger.error(...prefixes, ...args),
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export type CommonLogSink = CommonLogger | CommonLogWithLevelFunction
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* A plain object is copied once, at creation.
|
|
133
|
-
* A function is called on every log call, for context that changes after the logger is created
|
|
134
|
-
* (e.g a request context whose user is resolved later).
|
|
135
|
-
*/
|
|
136
|
-
export type CommonLogContext = AnyObject | (() => AnyObject)
|
|
137
|
-
|
|
138
|
-
export interface CommonLoggerWithContext extends CommonLogger {
|
|
139
|
-
child: (context: CommonLogContext) => CommonLoggerWithContext
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* Creates a CommonLogger from a single function that takes `level` and `args`.
|
|
144
|
-
*
|
|
145
|
-
* With a context, every call emits one object: plain-object args merged in, first Error as `err`,
|
|
146
|
-
* the rest joined into `msg`.
|
|
147
|
-
*/
|
|
148
|
-
export function commonLoggerCreate(sink: CommonLogSink): CommonLogger
|
|
149
|
-
export function commonLoggerCreate(
|
|
150
|
-
sink: CommonLogSink,
|
|
151
|
-
context: CommonLogContext,
|
|
152
|
-
): CommonLoggerWithContext
|
|
153
|
-
export function commonLoggerCreate(sink: CommonLogSink, context?: CommonLogContext): CommonLogger {
|
|
154
|
-
const fn: CommonLogWithLevelFunction =
|
|
155
|
-
typeof sink === 'function' ? sink : (level, args) => sink[level](...args)
|
|
156
|
-
|
|
157
|
-
if (!context) {
|
|
158
|
-
return {
|
|
159
|
-
debug: (...args) => fn('debug', args),
|
|
160
|
-
log: (...args) => fn('log', args),
|
|
161
|
-
warn: (...args) => fn('warn', args),
|
|
162
|
-
error: (...args) => fn('error', args),
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const getContext = contextGetter(context)
|
|
167
|
-
|
|
168
|
-
const logger: CommonLoggerWithContext = {
|
|
169
|
-
debug: (...args) => fn('debug', [toLogEntry(getContext(), args)]),
|
|
170
|
-
log: (...args) => fn('log', [toLogEntry(getContext(), args)]),
|
|
171
|
-
warn: (...args) => fn('warn', [toLogEntry(getContext(), args)]),
|
|
172
|
-
error: (...args) => fn('error', [toLogEntry(getContext(), args)]),
|
|
173
|
-
child: c => {
|
|
174
|
-
const getChild = contextGetter(c)
|
|
175
|
-
return commonLoggerCreate(fn, () => ({ ...getContext(), ...getChild() }))
|
|
176
|
-
},
|
|
141
|
+
debug: input => loggers.forEach(logger => logger.debug(input)),
|
|
142
|
+
log: input => loggers.forEach(logger => logger.log(input)),
|
|
143
|
+
warn: input => loggers.forEach(logger => logger.warn(input)),
|
|
144
|
+
error: input => loggers.forEach(logger => logger.error(input)),
|
|
145
|
+
child: context => commonLoggerPipe(loggers.map(logger => logger.child(context))),
|
|
177
146
|
}
|
|
178
|
-
return logger
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function contextGetter(context: CommonLogContext): () => AnyObject {
|
|
182
|
-
// AnyObject also admits functions, so typeof does not narrow the union
|
|
183
|
-
if (typeof context === 'function') return context as () => AnyObject
|
|
184
|
-
const copy = { ...context }
|
|
185
|
-
return () => copy
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function toLogEntry(context: AnyObject, args: any[]): AnyObject {
|
|
189
|
-
const entry: AnyObject = { ...context }
|
|
190
|
-
let err: Error | undefined
|
|
191
|
-
let msg: string | undefined
|
|
192
|
-
|
|
193
|
-
for (const arg of args) {
|
|
194
|
-
if (_isPlainObject(arg)) {
|
|
195
|
-
Object.assign(entry, arg)
|
|
196
|
-
} else if (!err && arg instanceof Error) {
|
|
197
|
-
err = arg
|
|
198
|
-
} else {
|
|
199
|
-
const s = String(arg)
|
|
200
|
-
msg = msg === undefined ? s : `${msg} ${s}`
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
if (err) entry['err'] = err
|
|
205
|
-
if (msg !== undefined) entry['msg'] = msg
|
|
206
|
-
return entry
|
|
207
147
|
}
|
package/src/promise/pMap.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ErrorMode } from '../error/errorMode.js'
|
|
2
|
+
import { consoleLogger } from '../log/commonLogger.js'
|
|
2
3
|
import type { CommonLogger } from '../log/commonLogger.js'
|
|
3
4
|
import { END, SKIP } from '../types.js'
|
|
4
5
|
import type { AbortableAsyncMapper } from '../types.js'
|
|
@@ -17,12 +18,12 @@ export interface PMapOptions {
|
|
|
17
18
|
* When set to THROW_AGGREGATED, instead of stopping when a promise rejects, it will wait for all the promises to settle and then reject with an Aggregated error.
|
|
18
19
|
*
|
|
19
20
|
* When set to SUPPRESS - will ignore errors and return results from successful operations.
|
|
20
|
-
* When in SUPPRESS - errors are still logged via the `logger` (which defaults to
|
|
21
|
+
* When in SUPPRESS - errors are still logged via the `logger` (which defaults to consoleLogger).
|
|
21
22
|
*/
|
|
22
23
|
errorMode?: ErrorMode
|
|
23
24
|
|
|
24
25
|
/**
|
|
25
|
-
* Default to `
|
|
26
|
+
* Default to `consoleLogger`.
|
|
26
27
|
* Pass `null` to skip logging completely.
|
|
27
28
|
*/
|
|
28
29
|
logger?: CommonLogger | null
|
|
@@ -71,7 +72,11 @@ export async function pMap<IN, OUT>(
|
|
|
71
72
|
const itemsLength = items.length
|
|
72
73
|
if (itemsLength === 0) return [] // short circuit
|
|
73
74
|
|
|
74
|
-
const {
|
|
75
|
+
const {
|
|
76
|
+
concurrency = Infinity,
|
|
77
|
+
errorMode = ErrorMode.THROW_IMMEDIATELY,
|
|
78
|
+
logger = consoleLogger,
|
|
79
|
+
} = opt
|
|
75
80
|
|
|
76
81
|
// Special cases that are able to preserve async stack traces
|
|
77
82
|
// Special case: serial execution
|
|
@@ -181,7 +186,7 @@ async function pMap1<IN, OUT>(
|
|
|
181
186
|
errors.push(err as Error)
|
|
182
187
|
} else {
|
|
183
188
|
// otherwise, suppress (but still log via logger)
|
|
184
|
-
logger?.error(err)
|
|
189
|
+
logger?.error({ err })
|
|
185
190
|
}
|
|
186
191
|
}
|
|
187
192
|
}
|
|
@@ -219,7 +224,7 @@ async function pMapAll<IN, OUT>(
|
|
|
219
224
|
errors.push(r.reason)
|
|
220
225
|
} else {
|
|
221
226
|
// otherwise, suppress (but still log via logger)
|
|
222
|
-
logger?.error(r.reason)
|
|
227
|
+
logger?.error({ err: r.reason })
|
|
223
228
|
}
|
|
224
229
|
}
|
|
225
230
|
|
package/src/promise/pQueue.ts
CHANGED
package/src/promise/pRetry.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { _since } from '../datetime/time.util.js'
|
|
2
2
|
import type { ErrorData } from '../error/error.model.js'
|
|
3
3
|
import { _errorDataAppend } from '../error/error.util.js'
|
|
4
|
+
import { consoleLogger } from '../log/commonLogger.js'
|
|
4
5
|
import type { CommonLogger } from '../log/commonLogger.js'
|
|
5
6
|
import type { AnyAsyncFunction, UnixTimestampMillis } from '../types.js'
|
|
6
7
|
import { pDelay } from './pDelay.js'
|
|
@@ -84,7 +85,7 @@ export interface PRetryOptions {
|
|
|
84
85
|
logNone?: boolean
|
|
85
86
|
|
|
86
87
|
/**
|
|
87
|
-
* Default to `
|
|
88
|
+
* Default to `consoleLogger`
|
|
88
89
|
*/
|
|
89
90
|
logger?: CommonLogger
|
|
90
91
|
|
|
@@ -113,7 +114,7 @@ export async function pRetry<T>(
|
|
|
113
114
|
delay: initialDelay = 1000,
|
|
114
115
|
delayMultiplier = 2,
|
|
115
116
|
predicate,
|
|
116
|
-
logger =
|
|
117
|
+
logger = consoleLogger,
|
|
117
118
|
name,
|
|
118
119
|
timeout,
|
|
119
120
|
} = opt
|
|
@@ -164,7 +165,7 @@ export async function pRetry<T>(
|
|
|
164
165
|
if (logFailures) {
|
|
165
166
|
// Logger at warn (not error) level, because it's not a fatal error, but a retriable one
|
|
166
167
|
// Fatal one is not logged either, because it's been thrown instead
|
|
167
|
-
logger.warn(`${fname} attempt #${attempt} error in ${_since(started)}
|
|
168
|
+
logger.warn({ msg: `${fname} attempt #${attempt} error in ${_since(started)}`, err })
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
if (attempt >= maxAttempts || (predicate && !predicate(err as Error, attempt, maxAttempts))) {
|