@kikiutils/shared 20.0.1 → 20.1.1
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/buffer.js.map +1 -1
- package/dist/classes/enhanced-date.d.ts +2 -2
- package/dist/classes/enhanced-date.d.ts.map +1 -1
- package/dist/classes/enhanced-date.js.map +1 -1
- package/dist/classes/path.d.ts +1 -1
- package/dist/classes/path.d.ts.map +1 -1
- package/dist/classes/path.js +30 -30
- package/dist/classes/path.js.map +1 -1
- package/dist/enum.d.ts +4 -3
- package/dist/enum.d.ts.map +1 -1
- package/dist/enum.js +3 -2
- package/dist/enum.js.map +1 -1
- package/dist/event-awaiter.d.ts.map +1 -1
- package/dist/event-awaiter.js +7 -6
- package/dist/event-awaiter.js.map +1 -1
- package/dist/general.d.ts +3 -2
- package/dist/general.d.ts.map +1 -1
- package/dist/general.js.map +1 -1
- package/dist/logger/_internals/formatter.d.ts +12 -4
- package/dist/logger/_internals/formatter.d.ts.map +1 -1
- package/dist/logger/_internals/formatter.js +12 -4
- package/dist/logger/_internals/formatter.js.map +1 -1
- package/dist/logger/_internals/logger.d.ts +6 -2
- package/dist/logger/_internals/logger.d.ts.map +1 -1
- package/dist/logger/_internals/logger.js +6 -3
- package/dist/logger/_internals/logger.js.map +1 -1
- package/dist/logger/node/_internals/file-writer.d.ts +3 -1
- package/dist/logger/node/_internals/file-writer.d.ts.map +1 -1
- package/dist/logger/node/_internals/file-writer.js +13 -9
- package/dist/logger/node/_internals/file-writer.js.map +1 -1
- package/dist/logger/node/_internals/formatter.d.ts +3 -1
- package/dist/logger/node/_internals/formatter.d.ts.map +1 -1
- package/dist/logger/node/_internals/formatter.js +3 -1
- package/dist/logger/node/_internals/formatter.js.map +1 -1
- package/dist/logger/node/index.js +11 -9
- package/dist/logger/node/index.js.map +1 -1
- package/dist/logger/node/types.d.ts +36 -12
- package/dist/logger/node/types.d.ts.map +1 -1
- package/dist/logger/types.d.ts +44 -19
- package/dist/logger/types.d.ts.map +1 -1
- package/dist/redis/script-runner.d.ts +1 -1
- package/dist/redis/script-runner.d.ts.map +1 -1
- package/dist/redis/script-runner.js.map +1 -1
- package/dist/storages/redis/keyed-store.d.ts.map +1 -1
- package/dist/storages/redis/keyed-store.js +10 -10
- package/dist/storages/redis/keyed-store.js.map +1 -1
- package/dist/storages/redis/msgpack.js +2 -2
- package/dist/storages/redis/msgpack.js.map +1 -1
- package/dist/time.d.ts.map +1 -1
- package/dist/time.js +4 -4
- package/dist/time.js.map +1 -1
- package/dist/types/filtered-key-path.d.ts +2 -2
- package/dist/types/filtered-key-path.d.ts.map +1 -1
- package/dist/types/index.d.ts +7 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/url.d.ts.map +1 -1
- package/dist/url.js.map +1 -1
- package/package.json +15 -11
- package/src/buffer.ts +0 -1
- package/src/classes/enhanced-date.ts +2 -2
- package/src/classes/path.ts +31 -31
- package/src/enum.ts +11 -5
- package/src/event-awaiter.ts +9 -7
- package/src/general.ts +5 -3
- package/src/logger/_internals/formatter.ts +12 -4
- package/src/logger/_internals/logger.ts +13 -4
- package/src/logger/node/_internals/file-writer.ts +13 -9
- package/src/logger/node/_internals/formatter.ts +3 -1
- package/src/logger/node/index.ts +13 -10
- package/src/logger/node/types.ts +36 -12
- package/src/logger/types.ts +45 -19
- package/src/redis/script-runner.ts +1 -1
- package/src/storages/redis/keyed-store.ts +10 -10
- package/src/storages/redis/msgpack.ts +2 -2
- package/src/time.ts +4 -4
- package/src/types/filtered-key-path.ts +2 -2
- package/src/types/index.ts +6 -0
- package/src/url.ts +0 -1
package/src/enum.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
objectKeys,
|
|
3
|
+
objectValues,
|
|
4
|
+
} from 'ts-extras';
|
|
5
|
+
import type { ValueOf } from 'type-fest';
|
|
6
|
+
|
|
1
7
|
/**
|
|
2
8
|
* Extracts only the numeric values from an enumeration-like object.
|
|
3
9
|
*
|
|
@@ -22,7 +28,7 @@
|
|
|
22
28
|
* console.log(getEnumNumberValues(RecordType)); // => [0, 1]
|
|
23
29
|
* ```
|
|
24
30
|
*/
|
|
25
|
-
export function getEnumNumberValues<T extends Record<string, any>>(enumObject: T): Extract<T
|
|
31
|
+
export function getEnumNumberValues<T extends Record<string, any>>(enumObject: T): Array<Extract<ValueOf<T>, number>> {
|
|
26
32
|
return getEnumValues(enumObject).filter((value) => typeof value === 'number');
|
|
27
33
|
}
|
|
28
34
|
|
|
@@ -50,7 +56,7 @@ export function getEnumNumberValues<T extends Record<string, any>>(enumObject: T
|
|
|
50
56
|
* console.log(getEnumStringValues(RecordType)); // => ['unknown']
|
|
51
57
|
* ```
|
|
52
58
|
*/
|
|
53
|
-
export function getEnumStringValues<T extends Record<string, any>>(enumObject: T): Extract<T
|
|
59
|
+
export function getEnumStringValues<T extends Record<string, any>>(enumObject: T): Array<Extract<ValueOf<T>, string>> {
|
|
54
60
|
return getEnumValues(enumObject).filter((value) => typeof value === 'string');
|
|
55
61
|
}
|
|
56
62
|
|
|
@@ -102,9 +108,9 @@ export function getEnumStringValues<T extends Record<string, any>>(enumObject: T
|
|
|
102
108
|
* console.log(getEnumValues(RecordType)); // => [0, 1, 'unknown']
|
|
103
109
|
* ```
|
|
104
110
|
*/
|
|
105
|
-
export function getEnumValues<T extends Record<string, any>>(enumObject: T):
|
|
106
|
-
const values =
|
|
111
|
+
export function getEnumValues<T extends Record<string, any>>(enumObject: T): Array<ValueOf<T>> {
|
|
112
|
+
const values = objectValues(enumObject);
|
|
107
113
|
const hasNumberValues = values.some((value) => typeof value === 'number');
|
|
108
114
|
if (!hasNumberValues) return values;
|
|
109
|
-
return
|
|
115
|
+
return objectKeys(enumObject).filter((key) => Number.isNaN(Number(key))).map((key) => enumObject[key]);
|
|
110
116
|
}
|
package/src/event-awaiter.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
export class EventAwaiter<T> {
|
|
13
13
|
// Private instance properties
|
|
14
|
-
#promiseResolvers = new Map<string, (
|
|
14
|
+
#promiseResolvers = new Map<string, Array<(value: PromiseLike<T | undefined> | T | undefined) => void>>();
|
|
15
15
|
|
|
16
16
|
// Public instance methods
|
|
17
17
|
|
|
@@ -58,8 +58,8 @@ export class EventAwaiter<T> {
|
|
|
58
58
|
*
|
|
59
59
|
* @throws Error through promise rejection if `strict` mode encounters an existing waiter.
|
|
60
60
|
*/
|
|
61
|
-
wait(key: string, timeoutMs?: number, mode?: 'override' | 'strict', signal?: AbortSignal) {
|
|
62
|
-
return new Promise<T | undefined>((resolve) => {
|
|
61
|
+
async wait(key: string, timeoutMs?: number, mode?: 'override' | 'strict', signal?: AbortSignal) {
|
|
62
|
+
return await new Promise<T | undefined>((resolve) => {
|
|
63
63
|
const resolvers = this.#promiseResolvers.get(key) || [];
|
|
64
64
|
if (resolvers.length) {
|
|
65
65
|
switch (mode) {
|
|
@@ -69,6 +69,8 @@ export class EventAwaiter<T> {
|
|
|
69
69
|
break;
|
|
70
70
|
case 'strict':
|
|
71
71
|
throw new Error(`Duplicate wait detected for key: ${key}`);
|
|
72
|
+
case undefined:
|
|
73
|
+
break;
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
@@ -120,8 +122,8 @@ export class EventAwaiter<T> {
|
|
|
120
122
|
*
|
|
121
123
|
* @see {@link EventAwaiter.wait}
|
|
122
124
|
*/
|
|
123
|
-
waitExclusive(key: string, timeoutMs?: number, signal?: AbortSignal) {
|
|
124
|
-
return this.wait(key, timeoutMs, 'strict', signal);
|
|
125
|
+
async waitExclusive(key: string, timeoutMs?: number, signal?: AbortSignal) {
|
|
126
|
+
return await this.wait(key, timeoutMs, 'strict', signal);
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
/**
|
|
@@ -138,7 +140,7 @@ export class EventAwaiter<T> {
|
|
|
138
140
|
*
|
|
139
141
|
* @see {@link EventAwaiter.wait}
|
|
140
142
|
*/
|
|
141
|
-
waitLatest(key: string, timeoutMs?: number, signal?: AbortSignal) {
|
|
142
|
-
return this.wait(key, timeoutMs, 'override', signal);
|
|
143
|
+
async waitLatest(key: string, timeoutMs?: number, signal?: AbortSignal) {
|
|
144
|
+
return await this.wait(key, timeoutMs, 'override', signal);
|
|
143
145
|
}
|
|
144
146
|
}
|
package/src/general.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Arrayable } from 'type-fest';
|
|
2
|
+
|
|
1
3
|
/* eslint-disable jsdoc/check-param-names */
|
|
2
4
|
|
|
3
5
|
/**
|
|
@@ -31,9 +33,9 @@
|
|
|
31
33
|
* console.log(extractFirstValue(undefined, 'fallback')); // => 'fallback'
|
|
32
34
|
* ```
|
|
33
35
|
*/
|
|
34
|
-
export function extractFirstValue<T>(value: T
|
|
35
|
-
export function extractFirstValue<T, D>(value: T
|
|
36
|
-
export function extractFirstValue<T, D>(value: T
|
|
36
|
+
export function extractFirstValue<T>(value: Arrayable<T>): T | undefined;
|
|
37
|
+
export function extractFirstValue<T, D>(value: Arrayable<T>, defaultValue: D): D | NonNullable<T>;
|
|
38
|
+
export function extractFirstValue<T, D>(value: Arrayable<T>, defaultValue?: D) {
|
|
37
39
|
return (Array.isArray(value) ? value[0] : value) ?? defaultValue;
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -6,7 +6,9 @@ import type { LoggerRecord } from './types';
|
|
|
6
6
|
|
|
7
7
|
// Functions
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/**
|
|
10
|
+
* Native console formatting keeps user strings literal and objects expandable.
|
|
11
|
+
*/
|
|
10
12
|
export function formatLoggerConsoleArgs(record: LoggerRecord): unknown[] {
|
|
11
13
|
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
12
14
|
const target = process[getLoggerStreamName(record.type)];
|
|
@@ -39,7 +41,9 @@ export function formatLoggerConsoleArgs(record: LoggerRecord): unknown[] {
|
|
|
39
41
|
];
|
|
40
42
|
}
|
|
41
43
|
|
|
42
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Keeps data arguments intact so browser developer tools can expand objects.
|
|
46
|
+
*/
|
|
43
47
|
export function formatLoggerParts(record: LoggerRecord, colors = false): unknown[] {
|
|
44
48
|
const parts: unknown[] = [];
|
|
45
49
|
const paint = (text: string, color: string) => colors ? `\u001B[${color}m${text}\u001B[0m` : text;
|
|
@@ -51,7 +55,9 @@ export function formatLoggerParts(record: LoggerRecord, colors = false): unknown
|
|
|
51
55
|
return parts;
|
|
52
56
|
}
|
|
53
57
|
|
|
54
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Fixed local-time representation, deliberately without a per-second cache.
|
|
60
|
+
*/
|
|
55
61
|
export function formatLoggerTime(date: Date): string {
|
|
56
62
|
const pad = (value: number, width = 2) => String(value).padStart(width, '0');
|
|
57
63
|
const day = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
@@ -59,7 +65,9 @@ export function formatLoggerTime(date: Date): string {
|
|
|
59
65
|
return `[${day} ${time}.${pad(date.getMilliseconds(), 3)}]`;
|
|
60
66
|
}
|
|
61
67
|
|
|
62
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Use native terminal detection; redirected streams still honor FORCE_COLOR.
|
|
70
|
+
*/
|
|
63
71
|
export function supportsLoggerColor(target: { hasColors?: () => boolean; isTTY?: boolean }): boolean {
|
|
64
72
|
if (target.hasColors) return target.hasColors();
|
|
65
73
|
const forced = process.env.FORCE_COLOR;
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
objectHasOwn,
|
|
3
|
+
objectKeys,
|
|
4
|
+
} from 'ts-extras';
|
|
5
|
+
|
|
1
6
|
import type {
|
|
2
7
|
Logger,
|
|
3
8
|
LoggerDiagnostic,
|
|
@@ -10,7 +15,9 @@ import { loggerMethods } from './levels';
|
|
|
10
15
|
import type { LoggerRecord } from './types';
|
|
11
16
|
|
|
12
17
|
interface LoggerHooks {
|
|
13
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* A non-throwing reporter created by createDiagnosticReporter.
|
|
20
|
+
*/
|
|
14
21
|
report: (event: LoggerDiagnostic) => void;
|
|
15
22
|
write: (record: LoggerRecord) => Promise<void> | void;
|
|
16
23
|
}
|
|
@@ -29,7 +36,9 @@ const stringPresentationKeys = [
|
|
|
29
36
|
|
|
30
37
|
// Functions
|
|
31
38
|
|
|
32
|
-
/**
|
|
39
|
+
/**
|
|
40
|
+
* A non-recursive, rate-limited diagnostic boundary shared by server and browser adapters.
|
|
41
|
+
*/
|
|
33
42
|
export function createDiagnosticReporter(
|
|
34
43
|
onError?: (event: Readonly<LoggerDiagnostic>) => void,
|
|
35
44
|
fallback?: (message: string) => void,
|
|
@@ -168,7 +177,7 @@ export function createLoggerCore(options: LoggerOptions, hooks: LoggerHooks): Re
|
|
|
168
177
|
},
|
|
169
178
|
} as Logger;
|
|
170
179
|
|
|
171
|
-
for (const type of
|
|
180
|
+
for (const type of objectKeys(loggerMethods) as LoggerMethod[]) {
|
|
172
181
|
logger[type] = async (...args: unknown[]) => {
|
|
173
182
|
try {
|
|
174
183
|
refreshLevel();
|
|
@@ -198,7 +207,7 @@ function getLevelSeverity(level: LoggerLevel | LoggerMethod): number {
|
|
|
198
207
|
|
|
199
208
|
function isLevel(value: unknown): value is LoggerLevel {
|
|
200
209
|
return typeof value === 'string'
|
|
201
|
-
&& (value === 'silent' || (value !== 'success' &&
|
|
210
|
+
&& (value === 'silent' || (value !== 'success' && objectHasOwn(loggerMethods, value)));
|
|
202
211
|
}
|
|
203
212
|
|
|
204
213
|
function readEnvironmentLevel(): string | undefined {
|
|
@@ -26,7 +26,9 @@ const defaultMaxPendingBytes = 8 * 1024 * 1024;
|
|
|
26
26
|
|
|
27
27
|
// Functions
|
|
28
28
|
|
|
29
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* A bounded admission layer over the stream's own ordered write buffer; not a second FIFO.
|
|
31
|
+
*/
|
|
30
32
|
export async function createFileWriter(
|
|
31
33
|
options: LoggerFileOptions,
|
|
32
34
|
report: (event: LoggerDiagnostic) => void,
|
|
@@ -139,8 +141,8 @@ export async function createFileWriter(
|
|
|
139
141
|
}
|
|
140
142
|
|
|
141
143
|
return Object.freeze({
|
|
142
|
-
close() {
|
|
143
|
-
if (closePromise) return closePromise;
|
|
144
|
+
async close() {
|
|
145
|
+
if (closePromise) return await closePromise;
|
|
144
146
|
closing = true;
|
|
145
147
|
closePromise = new Promise<void>((resolveClosed) => {
|
|
146
148
|
if (closed) {
|
|
@@ -156,21 +158,21 @@ export async function createFileWriter(
|
|
|
156
158
|
}
|
|
157
159
|
});
|
|
158
160
|
|
|
159
|
-
return closePromise;
|
|
161
|
+
return await closePromise;
|
|
160
162
|
},
|
|
161
163
|
getStats: () => Object.freeze({
|
|
162
164
|
droppedCount,
|
|
163
165
|
pendingBytes,
|
|
164
166
|
writeErrorCount,
|
|
165
167
|
}),
|
|
166
|
-
write(text: string) {
|
|
168
|
+
async write(text: string) {
|
|
167
169
|
if (closing || closed || failed) {
|
|
168
170
|
report({
|
|
169
171
|
message: 'File output is closed or unavailable.',
|
|
170
172
|
type: 'closed',
|
|
171
173
|
});
|
|
172
174
|
|
|
173
|
-
return Promise.resolve();
|
|
175
|
+
return await Promise.resolve();
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
const bytes = Buffer.byteLength(text);
|
|
@@ -182,11 +184,11 @@ export async function createFileWriter(
|
|
|
182
184
|
type: 'overflow',
|
|
183
185
|
});
|
|
184
186
|
|
|
185
|
-
return Promise.resolve();
|
|
187
|
+
return await Promise.resolve();
|
|
186
188
|
}
|
|
187
189
|
|
|
188
190
|
pendingBytes += bytes;
|
|
189
|
-
return new Promise<void>((resolveWritten) => {
|
|
191
|
+
return await new Promise<void>((resolveWritten) => {
|
|
190
192
|
let settled = false;
|
|
191
193
|
const settle = () => {
|
|
192
194
|
if (settled) return;
|
|
@@ -213,7 +215,9 @@ export async function createFileWriter(
|
|
|
213
215
|
});
|
|
214
216
|
}
|
|
215
217
|
|
|
216
|
-
/**
|
|
218
|
+
/**
|
|
219
|
+
* Validate before mkdir, stream creation, or other filesystem side effects.
|
|
220
|
+
*/
|
|
217
221
|
function validateFileOptions(options: LoggerFileOptions) {
|
|
218
222
|
if (
|
|
219
223
|
!options
|
|
@@ -5,7 +5,9 @@ import type { LoggerRecord } from '../../_internals/types';
|
|
|
5
5
|
|
|
6
6
|
// Functions
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Preserve user ANSI for console output without invoking custom inspect callbacks/getters.
|
|
10
|
+
*/
|
|
9
11
|
export function formatNodeLoggerRecord(record: LoggerRecord, colors = false) {
|
|
10
12
|
const message = record.args.map(
|
|
11
13
|
(arg) => typeof arg === 'string'
|
package/src/logger/node/index.ts
CHANGED
|
@@ -47,7 +47,7 @@ export async function createNodeLogger(options: NodeLoggerOptions = {}): Promise
|
|
|
47
47
|
options,
|
|
48
48
|
{
|
|
49
49
|
report,
|
|
50
|
-
write(record) {
|
|
50
|
+
async write(record) {
|
|
51
51
|
if (closed) {
|
|
52
52
|
report({
|
|
53
53
|
message: 'Logger is closed.',
|
|
@@ -69,14 +69,14 @@ export async function createNodeLogger(options: NodeLoggerOptions = {}): Promise
|
|
|
69
69
|
void completion.then(() => consoleWrites.delete(completion));
|
|
70
70
|
const fileCompletion = writer?.write(`${stripVTControlCharacters(text)}\n`);
|
|
71
71
|
return fileCompletion
|
|
72
|
-
? Promise.all([
|
|
72
|
+
? await Promise.all([
|
|
73
73
|
completion,
|
|
74
74
|
fileCompletion,
|
|
75
75
|
]).then(() => {})
|
|
76
|
-
: completion;
|
|
76
|
+
: await completion;
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
return writer?.write(`${stripVTControlCharacters(text)}\n`);
|
|
79
|
+
return await writer?.write(`${stripVTControlCharacters(text)}\n`);
|
|
80
80
|
},
|
|
81
81
|
},
|
|
82
82
|
);
|
|
@@ -85,8 +85,8 @@ export async function createNodeLogger(options: NodeLoggerOptions = {}): Promise
|
|
|
85
85
|
|
|
86
86
|
return Object.freeze({
|
|
87
87
|
...logger,
|
|
88
|
-
close() {
|
|
89
|
-
if (closePromise) return closePromise;
|
|
88
|
+
async close() {
|
|
89
|
+
if (closePromise) return await closePromise;
|
|
90
90
|
closed = true;
|
|
91
91
|
closePromise = (async () => {
|
|
92
92
|
await Promise.all([
|
|
@@ -107,7 +107,8 @@ export async function createNodeLogger(options: NodeLoggerOptions = {}): Promise
|
|
|
107
107
|
|
|
108
108
|
consoleTargets.clear();
|
|
109
109
|
})();
|
|
110
|
-
|
|
110
|
+
|
|
111
|
+
return await closePromise;
|
|
111
112
|
},
|
|
112
113
|
getStats: () => writer?.getStats() ?? Object.freeze({
|
|
113
114
|
droppedCount: 0,
|
|
@@ -117,8 +118,10 @@ export async function createNodeLogger(options: NodeLoggerOptions = {}): Promise
|
|
|
117
118
|
});
|
|
118
119
|
}
|
|
119
120
|
|
|
120
|
-
/**
|
|
121
|
-
|
|
121
|
+
/**
|
|
122
|
+
* One error listener per shared output stream; retained until its last owning logger calls close().
|
|
123
|
+
*/
|
|
124
|
+
async function writeConsole(
|
|
122
125
|
target: NodeJS.WriteStream,
|
|
123
126
|
text: string,
|
|
124
127
|
report: (event: LoggerDiagnostic) => void,
|
|
@@ -146,7 +149,7 @@ function writeConsole(
|
|
|
146
149
|
|
|
147
150
|
state.owners.add(owner);
|
|
148
151
|
const current = state;
|
|
149
|
-
return new Promise<void>((resolveWritten) => {
|
|
152
|
+
return await new Promise<void>((resolveWritten) => {
|
|
150
153
|
let settled = false;
|
|
151
154
|
const settle = (error?: Error | null) => {
|
|
152
155
|
if (settled) return;
|
package/src/logger/node/types.ts
CHANGED
|
@@ -4,50 +4,74 @@ import type {
|
|
|
4
4
|
LoggerOptions,
|
|
5
5
|
} from '../types';
|
|
6
6
|
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* Specify interval and/or maxSize. Size limits use positive integers with B, K, M, or G (binary units).
|
|
9
|
+
*/
|
|
8
10
|
export type LoggerRotationOptions = {
|
|
9
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Gzip historical files. Defaults to true.
|
|
13
|
+
*/
|
|
10
14
|
compress?: boolean;
|
|
11
15
|
|
|
12
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Number of historical files, excluding the active file. Defaults to 30.
|
|
18
|
+
*/
|
|
13
19
|
keepFiles?: number;
|
|
14
20
|
|
|
15
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Maximum combined size of historical files.
|
|
23
|
+
*/
|
|
16
24
|
maxTotalSize?: string;
|
|
17
25
|
} & (
|
|
18
26
|
| {
|
|
19
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Opt-in time rotation aligned to the system's local calendar boundary.
|
|
29
|
+
*/
|
|
20
30
|
interval: 'daily' | 'hourly';
|
|
21
31
|
maxSize?: string;
|
|
22
32
|
}
|
|
23
33
|
| {
|
|
24
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Opt-in time rotation aligned to the system's local calendar boundary.
|
|
36
|
+
*/
|
|
25
37
|
interval?: 'daily' | 'hourly';
|
|
26
38
|
maxSize: string;
|
|
27
39
|
}
|
|
28
40
|
);
|
|
29
41
|
|
|
30
42
|
export interface LoggerFileOptions {
|
|
31
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Maximum unfinished payload bytes, including data already submitted to the stream.
|
|
45
|
+
*/
|
|
32
46
|
maxPendingBytes?: number;
|
|
33
47
|
|
|
34
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Resolved once at creation. The file is opened in append mode.
|
|
50
|
+
*/
|
|
35
51
|
path: PathLike;
|
|
36
52
|
|
|
37
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* Omitted or false disables rotation. An object must specify interval or maxSize.
|
|
55
|
+
*/
|
|
38
56
|
rotate?: false | LoggerRotationOptions;
|
|
39
57
|
}
|
|
40
58
|
|
|
41
59
|
export interface NodeLogger extends Logger {
|
|
42
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Stop accepting records, finish accepted file writes, then release the stream. Not fsync.
|
|
62
|
+
*/
|
|
43
63
|
close: () => Promise<void>;
|
|
44
64
|
|
|
45
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* A fresh, frozen file-output snapshot. Dropped/failed records still resolve their logging promises.
|
|
67
|
+
*/
|
|
46
68
|
getStats: () => Readonly<NodeLoggerStats>;
|
|
47
69
|
}
|
|
48
70
|
|
|
49
71
|
export interface NodeLoggerOptions extends LoggerOptions {
|
|
50
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Disabled by default. Each logger owns at most one fixed file destination.
|
|
74
|
+
*/
|
|
51
75
|
file?: false | LoggerFileOptions;
|
|
52
76
|
}
|
|
53
77
|
|
package/src/logger/types.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import type { SafePromise } from '../types';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimum severity to emit; `success` uses the same threshold as `info`.
|
|
5
|
+
*/
|
|
2
6
|
export type LoggerLevel = 'debug' | 'error' | 'fatal' | 'info' | 'silent' | 'trace' | 'warn';
|
|
3
7
|
|
|
4
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* A logging method, including the informational success presentation.
|
|
10
|
+
*/
|
|
5
11
|
export type LoggerMethod = 'success' | Exclude<LoggerLevel, 'silent'>;
|
|
6
12
|
|
|
7
13
|
/**
|
|
@@ -13,48 +19,68 @@ export type LoggerMethod = 'success' | Exclude<LoggerLevel, 'silent'>;
|
|
|
13
19
|
*/
|
|
14
20
|
export interface Logger {
|
|
15
21
|
configure: (options: Pick<LoggerOptions, 'console' | 'icons' | 'prefix' | 'suffix' | 'timestamp'>) => void;
|
|
16
|
-
debug: (...args: unknown[]) =>
|
|
17
|
-
error: (...args: unknown[]) =>
|
|
18
|
-
fatal: (...args: unknown[]) =>
|
|
19
|
-
info: (...args: unknown[]) =>
|
|
22
|
+
debug: (...args: unknown[]) => SafePromise<void>;
|
|
23
|
+
error: (...args: unknown[]) => SafePromise<void>;
|
|
24
|
+
fatal: (...args: unknown[]) => SafePromise<void>;
|
|
25
|
+
info: (...args: unknown[]) => SafePromise<void>;
|
|
20
26
|
|
|
21
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* Accepts success as an informational method; it is not a configurable threshold.
|
|
29
|
+
*/
|
|
22
30
|
isLevelEnabled: (level: LoggerLevel | LoggerMethod) => boolean;
|
|
23
31
|
|
|
24
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Sets a severity threshold (not success); env restores environment-driven filtering.
|
|
34
|
+
*/
|
|
25
35
|
setLevel: (level: 'env' | LoggerLevel) => void;
|
|
26
|
-
success: (...args: unknown[]) =>
|
|
27
|
-
trace: (...args: unknown[]) =>
|
|
28
|
-
warn: (...args: unknown[]) =>
|
|
36
|
+
success: (...args: unknown[]) => SafePromise<void>;
|
|
37
|
+
trace: (...args: unknown[]) => SafePromise<void>;
|
|
38
|
+
warn: (...args: unknown[]) => SafePromise<void>;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
|
-
/**
|
|
41
|
+
/**
|
|
42
|
+
* A shallow-frozen failure notification; nested error values retain their original ownership.
|
|
43
|
+
*/
|
|
32
44
|
export interface LoggerDiagnostic {
|
|
33
45
|
droppedCount?: number;
|
|
34
46
|
error?: unknown;
|
|
35
47
|
message: string;
|
|
36
48
|
|
|
37
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Same-type notifications suppressed since the previous emitted diagnostic.
|
|
51
|
+
*/
|
|
38
52
|
suppressedCount?: number;
|
|
39
53
|
type: 'closed' | 'console' | 'file' | 'format' | 'invalid-level' | 'overflow';
|
|
40
54
|
}
|
|
41
55
|
|
|
42
|
-
/**
|
|
56
|
+
/**
|
|
57
|
+
* Configuration shared by browser and server loggers.
|
|
58
|
+
*/
|
|
43
59
|
export interface LoggerOptions {
|
|
44
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Enables console output. Defaults to `true`.
|
|
62
|
+
*/
|
|
45
63
|
console?: boolean;
|
|
46
64
|
|
|
47
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Enables Unicode level icons. Defaults to `true`.
|
|
67
|
+
*/
|
|
48
68
|
icons?: boolean;
|
|
49
69
|
|
|
50
|
-
/**
|
|
70
|
+
/**
|
|
71
|
+
* Overrides LOG_LEVEL; `env` restores the environment-driven threshold.
|
|
72
|
+
*/
|
|
51
73
|
level?: 'env' | LoggerLevel;
|
|
52
74
|
|
|
53
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Best-effort, rate-limited notification; failures in this callback are contained.
|
|
77
|
+
*/
|
|
54
78
|
onError?: (event: Readonly<LoggerDiagnostic>) => void;
|
|
55
79
|
prefix?: string;
|
|
56
80
|
suffix?: string;
|
|
57
81
|
|
|
58
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Enables the fixed local-time millisecond prefix. Defaults to `true`.
|
|
84
|
+
*/
|
|
59
85
|
timestamp?: boolean;
|
|
60
86
|
}
|
|
@@ -14,7 +14,7 @@ import { createHash } from 'node:crypto';
|
|
|
14
14
|
*
|
|
15
15
|
* @returns A promise resolving to the Redis script result after execution completes.
|
|
16
16
|
*/
|
|
17
|
-
export type RedisScriptRunner<T = unknown> = (keys: readonly string[], args:
|
|
17
|
+
export type RedisScriptRunner<T = unknown> = (keys: readonly string[], args: ReadonlyArray<number | string>) => Promise<
|
|
18
18
|
T
|
|
19
19
|
>;
|
|
20
20
|
|
|
@@ -20,18 +20,18 @@ import type { RedisLikeStorage } from './types';
|
|
|
20
20
|
*/
|
|
21
21
|
export function createRedisKeyedStore<D = unknown>(storage: MaybeReadonly<RedisLikeStorage>) {
|
|
22
22
|
return <P extends any[]>(keyFn: (...args: P) => string) => Object.freeze({
|
|
23
|
-
getAndRemoveItem: (...args: P) => storage.getAndRemoveItem<D>(keyFn(...args)),
|
|
24
|
-
getItem: (...args: P) => storage.getItem<D>(keyFn(...args)),
|
|
25
|
-
getItemTtl: (...args: P) => storage.getItemTtl(keyFn(...args)),
|
|
26
|
-
hasItem: (...args: P) => storage.hasItem(keyFn(...args)),
|
|
27
|
-
removeItem: (...args: P) => storage.removeItem(keyFn(...args)),
|
|
23
|
+
getAndRemoveItem: async (...args: P) => await storage.getAndRemoveItem<D>(keyFn(...args)),
|
|
24
|
+
getItem: async (...args: P) => await storage.getItem<D>(keyFn(...args)),
|
|
25
|
+
getItemTtl: async (...args: P) => await storage.getItemTtl(keyFn(...args)),
|
|
26
|
+
hasItem: async (...args: P) => await storage.hasItem(keyFn(...args)),
|
|
27
|
+
removeItem: async (...args: P) => await storage.removeItem(keyFn(...args)),
|
|
28
28
|
resolveKey: (...args: P) => keyFn(...args),
|
|
29
|
-
setItem: (value: D, ...args: P) => storage.setItem(keyFn(...args), value),
|
|
30
|
-
setItemWithTtl(ttlSeconds: number, value: D, ...args: P) {
|
|
31
|
-
return storage.setItemWithTtl(keyFn(...args), ttlSeconds, value);
|
|
29
|
+
setItem: async (value: D, ...args: P) => await storage.setItem(keyFn(...args), value),
|
|
30
|
+
async setItemWithTtl(ttlSeconds: number, value: D, ...args: P) {
|
|
31
|
+
return await storage.setItemWithTtl(keyFn(...args), ttlSeconds, value);
|
|
32
32
|
},
|
|
33
|
-
setItemWithTtlIfAbsent(ttlSeconds: number, value: D, ...args: P) {
|
|
34
|
-
return storage.setItemWithTtlIfAbsent(keyFn(...args), ttlSeconds, value);
|
|
33
|
+
async setItemWithTtlIfAbsent(ttlSeconds: number, value: D, ...args: P) {
|
|
34
|
+
return await storage.setItemWithTtlIfAbsent(keyFn(...args), ttlSeconds, value);
|
|
35
35
|
},
|
|
36
36
|
});
|
|
37
37
|
}
|
|
@@ -73,8 +73,8 @@ export function createRedisMsgpackStorage(adapter: RedisLikeAdapter): Readonly<R
|
|
|
73
73
|
const rawValue = await adapter.getBuffer(key);
|
|
74
74
|
return rawValue ? decode(rawValue) as T : null;
|
|
75
75
|
},
|
|
76
|
-
getItemTtl: (key: string) => adapter.ttl(key),
|
|
77
|
-
hasItem: (key: string) => adapter.has(key),
|
|
76
|
+
getItemTtl: async (key: string) => await adapter.ttl(key),
|
|
77
|
+
hasItem: async (key: string) => await adapter.has(key),
|
|
78
78
|
removeItem: async (key: string) => await adapter.delete(key) === 1,
|
|
79
79
|
setItem: async (key: string, value: any) => await adapter.setBuffer(key, encode(value)) === 'OK',
|
|
80
80
|
async setItemWithTtl(key: string, ttlSeconds: number, value: any) {
|
package/src/time.ts
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* await delay(5000, controller.signal);
|
|
19
19
|
* ```
|
|
20
20
|
*/
|
|
21
|
-
export function delay(ms: number, signal?: AbortSignal): Promise<void> {
|
|
22
|
-
return new Promise((resolve) => {
|
|
21
|
+
export async function delay(ms: number, signal?: AbortSignal): Promise<void> {
|
|
22
|
+
return await new Promise((resolve) => {
|
|
23
23
|
if (signal?.aborted) {
|
|
24
24
|
resolve();
|
|
25
25
|
return;
|
|
@@ -61,8 +61,8 @@ export function delay(ms: number, signal?: AbortSignal): Promise<void> {
|
|
|
61
61
|
* }
|
|
62
62
|
* ```
|
|
63
63
|
*/
|
|
64
|
-
export function delayOrThrow(ms: number, signal?: AbortSignal): Promise<void> {
|
|
65
|
-
return new Promise((resolve, reject) => {
|
|
64
|
+
export async function delayOrThrow(ms: number, signal?: AbortSignal): Promise<void> {
|
|
65
|
+
return await new Promise((resolve, reject) => {
|
|
66
66
|
if (signal?.aborted) {
|
|
67
67
|
reject(signal.reason);
|
|
68
68
|
return;
|
|
@@ -22,7 +22,7 @@ type DefaultPath<
|
|
|
22
22
|
export type FilteredKeyPath<T, U = never> = T extends ReadonlyArray<infer V> ? (IsTuple<T> extends true ? { [K in TupleKey<T>]-?: PathImpl<Exclude<K, symbol>, T[K], U> }[TupleKey<T>] : PathImpl<number, V, U>) : { [K in keyof T]-?: PathImpl<Exclude<K, symbol>, T[K], U> }[keyof T];
|
|
23
23
|
/* eslint-enable style/max-len */
|
|
24
24
|
|
|
25
|
-
type IsTuple<T extends
|
|
25
|
+
type IsTuple<T extends readonly any[]> = number extends T['length'] ? false : true;
|
|
26
26
|
type PathImpl<K extends number | string, V, U> = [U] extends [never] ? DefaultPath<K, V> : ConditionalPath<K, V, U>;
|
|
27
27
|
type TerminalType =
|
|
28
28
|
| bigint
|
|
@@ -38,4 +38,4 @@ type TerminalType =
|
|
|
38
38
|
| symbol
|
|
39
39
|
| undefined;
|
|
40
40
|
|
|
41
|
-
type TupleKey<T extends
|
|
41
|
+
type TupleKey<T extends readonly any[]> = Exclude<keyof T, keyof any[]>;
|
package/src/types/index.ts
CHANGED
|
@@ -40,3 +40,9 @@ export type ReadonlyPartialRecord<K extends keyof any, T> = Readonly<PartialReco
|
|
|
40
40
|
* A readonly `Record`.
|
|
41
41
|
*/
|
|
42
42
|
export type ReadonlyRecord<K extends keyof any, T> = Readonly<Record<K, T>>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* A promise whose rejection is handled internally and can safely be left unawaited.
|
|
46
|
+
* This type only marks that contract; it does not add runtime rejection handling.
|
|
47
|
+
*/
|
|
48
|
+
export type SafePromise<T> = Promise<T> & { readonly __safePromise?: true };
|
package/src/url.ts
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
* @throws Error if `redirectPath` is not a safe same-origin application path.
|
|
13
13
|
*/
|
|
14
14
|
export function appendRedirectParamToUrl(url: string, redirectPath: string) {
|
|
15
|
-
// eslint-disable-next-line style/max-len
|
|
16
15
|
if (!isSafeRedirectPath(redirectPath)) throw new Error(`Invalid redirect path: "${redirectPath}". Redirect paths must be safe same-origin paths starting with '/'.`);
|
|
17
16
|
|
|
18
17
|
const [baseAndHash, rawQuery = ''] = url.split('?');
|