@kikiutils/shared 20.1.0 → 20.1.2
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/classes/path.d.ts.map +1 -1
- package/dist/classes/path.js +30 -30
- package/dist/classes/path.js.map +1 -1
- package/dist/classes/precision-number.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/env.js.map +1 -1
- package/dist/event-awaiter.d.ts.map +1 -1
- package/dist/event-awaiter.js +6 -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/logger.d.ts.map +1 -1
- package/dist/logger/_internals/logger.js +3 -2
- package/dist/logger/_internals/logger.js.map +1 -1
- package/dist/logger/node/_internals/file-writer.js +7 -7
- package/dist/logger/node/_internals/file-writer.js.map +1 -1
- package/dist/logger/node/index.js +8 -8
- package/dist/logger/node/index.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/package.json +46 -26
- package/src/classes/path.ts +30 -30
- package/src/enum.ts +11 -5
- package/src/event-awaiter.ts +6 -6
- package/src/general.ts +5 -3
- package/src/logger/_internals/logger.ts +7 -2
- package/src/logger/node/_internals/file-writer.ts +7 -7
- package/src/logger/node/index.ts +9 -9
- package/src/storages/redis/keyed-store.ts +10 -10
- package/src/storages/redis/msgpack.ts +2 -2
- package/src/time.ts +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.js","names":[],"sources":["../../../src/logger/_internals/logger.ts"],"sourcesContent":["import type {\n Logger,\n LoggerDiagnostic,\n LoggerLevel,\n LoggerMethod,\n LoggerOptions,\n} from '../types';\n\nimport { loggerMethods } from './levels';\nimport type { LoggerRecord } from './types';\n\ninterface LoggerHooks {\n /**\n * A non-throwing reporter created by createDiagnosticReporter.\n */\n report: (event: LoggerDiagnostic) => void;\n write: (record: LoggerRecord) => Promise<void> | void;\n}\n\n// Constants/Variables\nconst booleanPresentationKeys = [\n 'console',\n 'icons',\n 'timestamp',\n] as const;\n\nconst stringPresentationKeys = [\n 'prefix',\n 'suffix',\n] as const;\n\n// Functions\n\n/**\n * A non-recursive, rate-limited diagnostic boundary shared by server and browser adapters.\n */\nexport function createDiagnosticReporter(\n onError?: (event: Readonly<LoggerDiagnostic>) => void,\n fallback?: (message: string) => void,\n): (event: LoggerDiagnostic) => void {\n let reporting = false;\n let callbackPending = false;\n const stateByType = new Map<LoggerDiagnostic['type'], { lastReportedAt: number; suppressedCount: number }>();\n\n return (event) => {\n if (reporting) return;\n try {\n const now = Date.now();\n const state = stateByType.get(event.type);\n if (state && now - state.lastReportedAt < 1000) {\n state.suppressedCount++;\n return;\n }\n\n reporting = true;\n const suppressedCount = state?.suppressedCount ?? 0;\n if (suppressedCount) {\n event = {\n ...event,\n suppressedCount,\n };\n }\n\n stateByType.set(\n event.type,\n {\n lastReportedAt: now,\n suppressedCount: 0,\n },\n );\n\n try {\n const summary = suppressedCount ? ` (${suppressedCount} similar diagnostics suppressed)` : '';\n fallback?.(`[logger] ${event.message}${summary}\\n`);\n } catch {\n // Diagnostics must never re-enter the normal logging pipeline.\n }\n\n if (!callbackPending) {\n try {\n callbackPending = true;\n const result: unknown = onError?.(Object.freeze({ ...event }));\n if (result !== null && (typeof result === 'object' || typeof result === 'function')) {\n void Promise.resolve(result).then(\n () => {\n callbackPending = false;\n },\n () => {\n callbackPending = false;\n },\n );\n } else {\n callbackPending = false;\n }\n } catch {\n callbackPending = false;\n // User diagnostic callbacks have the same non-throwing boundary.\n }\n }\n } catch { /* Diagnostics must never escape into the logging pipeline. */ } finally {\n reporting = false;\n }\n };\n}\n\nexport function createLoggerCore(options: LoggerOptions, hooks: LoggerHooks): Readonly<Logger> {\n validateLevel(options.level);\n validatePresentation(options);\n if (options.onError !== undefined && typeof options.onError !== 'function') {\n throw new TypeError('Logger onError must be a function');\n }\n\n const presentation = {\n console: options.console ?? true,\n icons: options.icons ?? true,\n prefix: options.prefix ?? '',\n suffix: options.suffix ?? '',\n timestamp: options.timestamp ?? true,\n };\n\n const { report } = hooks;\n\n let override = options.level === 'env' ? undefined : options.level;\n let previousEnvironment: string | undefined;\n let currentLevel: LoggerLevel = override ?? 'info';\n let environmentRead = false;\n\n function refreshLevel() {\n if (override !== undefined) return;\n const input = readEnvironmentLevel();\n if (environmentRead && input === previousEnvironment) return;\n environmentRead = true;\n previousEnvironment = input;\n const normalized = input?.trim().toLowerCase() || undefined;\n currentLevel = isLevel(normalized) ? normalized : 'info';\n if (normalized !== undefined && !isLevel(normalized)) {\n report({\n message: 'Invalid LOG_LEVEL; using info',\n type: 'invalid-level',\n });\n }\n }\n\n const logger = {\n configure(configuration) {\n validatePresentation(configuration);\n for (const name of booleanPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n\n for (const name of stringPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n },\n isLevelEnabled(level) {\n if (level !== 'success' && !isLevel(level)) throw new TypeError('Invalid logger level');\n refreshLevel();\n return level !== 'silent' && getLevelSeverity(level) >= getLevelSeverity(currentLevel);\n },\n setLevel(level) {\n validateLevel(level);\n if (level === undefined) throw new TypeError('Invalid logger level');\n override = level === 'env' ? undefined : level;\n environmentRead = false;\n if (override !== undefined) {\n currentLevel = override;\n } else {\n refreshLevel();\n }\n },\n } as Logger;\n\n for (const type of Object.keys(loggerMethods) as LoggerMethod[]) {\n logger[type] = async (...args: unknown[]) => {\n try {\n refreshLevel();\n if (loggerMethods[type].severity < getLevelSeverity(currentLevel)) return;\n await hooks.write({\n ...presentation,\n args,\n date: new Date(),\n type,\n });\n } catch (error) {\n report({\n error,\n message: 'Logger output failed',\n type: 'format',\n });\n }\n };\n }\n\n return Object.freeze(logger);\n}\n\nfunction getLevelSeverity(level: LoggerLevel | LoggerMethod): number {\n return level === 'silent' ? Number.POSITIVE_INFINITY : loggerMethods[level].severity;\n}\n\nfunction isLevel(value: unknown): value is LoggerLevel {\n return typeof value === 'string'\n && (value === 'silent' || (value !== 'success' && Object.hasOwn(loggerMethods, value)));\n}\n\nfunction readEnvironmentLevel(): string | undefined {\n return (globalThis as { process?: { env?: { LOG_LEVEL?: string } } }).process?.env?.LOG_LEVEL;\n}\n\nfunction validateLevel(value: unknown): asserts value is 'env' | LoggerLevel | undefined {\n if (value !== undefined && value !== 'env' && !isLevel(value)) throw new TypeError('Invalid logger level');\n}\n\nfunction validatePresentation(options: Pick<LoggerOptions, 'console' | 'icons' | 'prefix' | 'suffix' | 'timestamp'>) {\n for (const name of booleanPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'boolean') {\n throw new TypeError(`Logger ${name} must be a boolean`);\n }\n }\n\n for (const name of stringPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'string') {\n throw new TypeError(`Logger ${name} must be a string`);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"logger.js","names":[],"sources":["../../../src/logger/_internals/logger.ts"],"sourcesContent":["import {\n objectHasOwn,\n objectKeys,\n} from 'ts-extras';\n\nimport type {\n Logger,\n LoggerDiagnostic,\n LoggerLevel,\n LoggerMethod,\n LoggerOptions,\n} from '../types';\n\nimport { loggerMethods } from './levels';\nimport type { LoggerRecord } from './types';\n\ninterface LoggerHooks {\n /**\n * A non-throwing reporter created by createDiagnosticReporter.\n */\n report: (event: LoggerDiagnostic) => void;\n write: (record: LoggerRecord) => Promise<void> | void;\n}\n\n// Constants/Variables\nconst booleanPresentationKeys = [\n 'console',\n 'icons',\n 'timestamp',\n] as const;\n\nconst stringPresentationKeys = [\n 'prefix',\n 'suffix',\n] as const;\n\n// Functions\n\n/**\n * A non-recursive, rate-limited diagnostic boundary shared by server and browser adapters.\n */\nexport function createDiagnosticReporter(\n onError?: (event: Readonly<LoggerDiagnostic>) => void,\n fallback?: (message: string) => void,\n): (event: LoggerDiagnostic) => void {\n let reporting = false;\n let callbackPending = false;\n const stateByType = new Map<LoggerDiagnostic['type'], { lastReportedAt: number; suppressedCount: number }>();\n\n return (event) => {\n if (reporting) return;\n try {\n const now = Date.now();\n const state = stateByType.get(event.type);\n if (state && now - state.lastReportedAt < 1000) {\n state.suppressedCount++;\n return;\n }\n\n reporting = true;\n const suppressedCount = state?.suppressedCount ?? 0;\n if (suppressedCount) {\n event = {\n ...event,\n suppressedCount,\n };\n }\n\n stateByType.set(\n event.type,\n {\n lastReportedAt: now,\n suppressedCount: 0,\n },\n );\n\n try {\n const summary = suppressedCount ? ` (${suppressedCount} similar diagnostics suppressed)` : '';\n fallback?.(`[logger] ${event.message}${summary}\\n`);\n } catch {\n // Diagnostics must never re-enter the normal logging pipeline.\n }\n\n if (!callbackPending) {\n try {\n callbackPending = true;\n const result: unknown = onError?.(Object.freeze({ ...event }));\n if (result !== null && (typeof result === 'object' || typeof result === 'function')) {\n void Promise.resolve(result).then(\n () => {\n callbackPending = false;\n },\n () => {\n callbackPending = false;\n },\n );\n } else {\n callbackPending = false;\n }\n } catch {\n callbackPending = false;\n // User diagnostic callbacks have the same non-throwing boundary.\n }\n }\n } catch { /* Diagnostics must never escape into the logging pipeline. */ } finally {\n reporting = false;\n }\n };\n}\n\nexport function createLoggerCore(options: LoggerOptions, hooks: LoggerHooks): Readonly<Logger> {\n validateLevel(options.level);\n validatePresentation(options);\n if (options.onError !== undefined && typeof options.onError !== 'function') {\n throw new TypeError('Logger onError must be a function');\n }\n\n const presentation = {\n console: options.console ?? true,\n icons: options.icons ?? true,\n prefix: options.prefix ?? '',\n suffix: options.suffix ?? '',\n timestamp: options.timestamp ?? true,\n };\n\n const { report } = hooks;\n\n let override = options.level === 'env' ? undefined : options.level;\n let previousEnvironment: string | undefined;\n let currentLevel: LoggerLevel = override ?? 'info';\n let environmentRead = false;\n\n function refreshLevel() {\n if (override !== undefined) return;\n const input = readEnvironmentLevel();\n if (environmentRead && input === previousEnvironment) return;\n environmentRead = true;\n previousEnvironment = input;\n const normalized = input?.trim().toLowerCase() || undefined;\n currentLevel = isLevel(normalized) ? normalized : 'info';\n if (normalized !== undefined && !isLevel(normalized)) {\n report({\n message: 'Invalid LOG_LEVEL; using info',\n type: 'invalid-level',\n });\n }\n }\n\n const logger = {\n configure(configuration) {\n validatePresentation(configuration);\n for (const name of booleanPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n\n for (const name of stringPresentationKeys) {\n const value = configuration[name];\n if (value !== undefined) presentation[name] = value;\n }\n },\n isLevelEnabled(level) {\n if (level !== 'success' && !isLevel(level)) throw new TypeError('Invalid logger level');\n refreshLevel();\n return level !== 'silent' && getLevelSeverity(level) >= getLevelSeverity(currentLevel);\n },\n setLevel(level) {\n validateLevel(level);\n if (level === undefined) throw new TypeError('Invalid logger level');\n override = level === 'env' ? undefined : level;\n environmentRead = false;\n if (override !== undefined) {\n currentLevel = override;\n } else {\n refreshLevel();\n }\n },\n } as Logger;\n\n for (const type of objectKeys(loggerMethods) as LoggerMethod[]) {\n logger[type] = async (...args: unknown[]) => {\n try {\n refreshLevel();\n if (loggerMethods[type].severity < getLevelSeverity(currentLevel)) return;\n await hooks.write({\n ...presentation,\n args,\n date: new Date(),\n type,\n });\n } catch (error) {\n report({\n error,\n message: 'Logger output failed',\n type: 'format',\n });\n }\n };\n }\n\n return Object.freeze(logger);\n}\n\nfunction getLevelSeverity(level: LoggerLevel | LoggerMethod): number {\n return level === 'silent' ? Number.POSITIVE_INFINITY : loggerMethods[level].severity;\n}\n\nfunction isLevel(value: unknown): value is LoggerLevel {\n return typeof value === 'string'\n && (value === 'silent' || (value !== 'success' && objectHasOwn(loggerMethods, value)));\n}\n\nfunction readEnvironmentLevel(): string | undefined {\n return (globalThis as { process?: { env?: { LOG_LEVEL?: string } } }).process?.env?.LOG_LEVEL;\n}\n\nfunction validateLevel(value: unknown): asserts value is 'env' | LoggerLevel | undefined {\n if (value !== undefined && value !== 'env' && !isLevel(value)) throw new TypeError('Invalid logger level');\n}\n\nfunction validatePresentation(options: Pick<LoggerOptions, 'console' | 'icons' | 'prefix' | 'suffix' | 'timestamp'>) {\n for (const name of booleanPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'boolean') {\n throw new TypeError(`Logger ${name} must be a boolean`);\n }\n }\n\n for (const name of stringPresentationKeys) {\n if (options[name] !== undefined && typeof options[name] !== 'string') {\n throw new TypeError(`Logger ${name} must be a string`);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -86,8 +86,8 @@ async function createFileWriter(options, report) {
|
|
|
86
86
|
initializing = false;
|
|
87
87
|
}
|
|
88
88
|
return Object.freeze({
|
|
89
|
-
close() {
|
|
90
|
-
if (closePromise) return closePromise;
|
|
89
|
+
async close() {
|
|
90
|
+
if (closePromise) return await closePromise;
|
|
91
91
|
closing = true;
|
|
92
92
|
closePromise = new Promise((resolveClosed) => {
|
|
93
93
|
if (closed) {
|
|
@@ -101,20 +101,20 @@ async function createFileWriter(options, report) {
|
|
|
101
101
|
fail(error);
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
|
-
return closePromise;
|
|
104
|
+
return await closePromise;
|
|
105
105
|
},
|
|
106
106
|
getStats: () => Object.freeze({
|
|
107
107
|
droppedCount,
|
|
108
108
|
pendingBytes,
|
|
109
109
|
writeErrorCount
|
|
110
110
|
}),
|
|
111
|
-
write(text) {
|
|
111
|
+
async write(text) {
|
|
112
112
|
if (closing || closed || failed) {
|
|
113
113
|
report({
|
|
114
114
|
message: "File output is closed or unavailable.",
|
|
115
115
|
type: "closed"
|
|
116
116
|
});
|
|
117
|
-
return Promise.resolve();
|
|
117
|
+
return await Promise.resolve();
|
|
118
118
|
}
|
|
119
119
|
const bytes = Buffer.byteLength(text);
|
|
120
120
|
if (bytes > maxPendingBytes - pendingBytes) {
|
|
@@ -124,10 +124,10 @@ async function createFileWriter(options, report) {
|
|
|
124
124
|
message: "File output queue is full; the newest record was discarded.",
|
|
125
125
|
type: "overflow"
|
|
126
126
|
});
|
|
127
|
-
return Promise.resolve();
|
|
127
|
+
return await Promise.resolve();
|
|
128
128
|
}
|
|
129
129
|
pendingBytes += bytes;
|
|
130
|
-
return new Promise((resolveWritten) => {
|
|
130
|
+
return await new Promise((resolveWritten) => {
|
|
131
131
|
let settled = false;
|
|
132
132
|
const settle = () => {
|
|
133
133
|
if (settled) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-writer.js","names":[],"sources":["../../../../src/logger/node/_internals/file-writer.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { createWriteStream } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport {\n basename,\n dirname,\n} from 'node:path';\nimport type { Writable } from 'node:stream';\nimport { fileURLToPath } from 'node:url';\n\nimport { Path } from '../../../classes/path';\nimport type { LoggerDiagnostic } from '../../types';\nimport type {\n LoggerFileOptions,\n NodeLoggerStats,\n} from '../types';\n\ninterface LoggerFileWriter {\n close: () => Promise<void>;\n getStats: () => Readonly<NodeLoggerStats>;\n write: (text: string) => Promise<void>;\n}\n\n// Constants/Variables\nconst defaultMaxPendingBytes = 8 * 1024 * 1024;\n\n// Functions\n\n/**\n * A bounded admission layer over the stream's own ordered write buffer; not a second FIFO.\n */\nexport async function createFileWriter(\n options: LoggerFileOptions,\n report: (event: LoggerDiagnostic) => void,\n): Promise<Readonly<LoggerFileWriter>> {\n const {\n maxPendingBytes,\n path,\n rotate,\n } = validateFileOptions(options);\n\n const createRotatingStream = rotate ? (await import('rotating-file-stream')).createStream : undefined;\n await mkdir(dirname(path), { recursive: true });\n const stream: Writable = rotate && createRotatingStream\n ? createRotatingStream(\n basename(path),\n {\n compress: rotate.compress === false ? false : 'gzip',\n ...rotate.interval === undefined ? {} : { interval: rotate.interval === 'hourly' ? '1h' : '1d' },\n intervalBoundary: true,\n intervalUTC: false,\n maxFiles: rotate.keepFiles ?? 30,\n ...rotate.maxTotalSize === undefined ? {} : { maxSize: rotate.maxTotalSize },\n path: dirname(path),\n ...rotate.maxSize === undefined ? {} : { size: rotate.maxSize },\n },\n )\n : createWriteStream(path, { flags: 'a' });\n\n let pendingBytes = 0;\n let droppedCount = 0;\n let writeErrorCount = 0;\n let failed = false;\n let closing = false;\n let closed = false;\n let closePromise: Promise<void> | undefined;\n let initializing = true;\n const unfinished = new Set<() => void>();\n let rejectInitialization: ((error: unknown) => void) | undefined;\n\n const settleAll = () => {\n for (const settle of unfinished) settle();\n };\n\n const fail = (error: unknown) => {\n rejectInitialization?.(error);\n if (!failed) {\n failed = true;\n if (!initializing) {\n writeErrorCount++;\n report({\n error,\n message: 'File output failed and has been disabled.',\n type: 'file',\n });\n }\n }\n\n settleAll();\n if (!stream.destroyed) stream.destroy();\n };\n\n stream.on('error', fail);\n stream.on(\n 'warning',\n (error: unknown) => {\n writeErrorCount++;\n report({\n error,\n message: 'File rotation cleanup failed.',\n type: 'file',\n });\n },\n );\n\n stream.on(\n 'close',\n () => {\n closed = true;\n if (!closing && !failed) fail(new Error('File stream closed unexpectedly.'));\n settleAll();\n },\n );\n\n // A write callback also waits for RFS's asynchronous initialization and initial rotation.\n try {\n await new Promise<void>((resolveReady, rejectReady) => {\n rejectInitialization = rejectReady;\n stream.write(\n Buffer.alloc(0),\n (error) => {\n if (error) rejectReady(error);\n else resolveReady();\n },\n );\n });\n\n if (failed || closed) throw new Error('File output failed during initialization.');\n } catch (error) {\n if (!stream.destroyed) stream.destroy();\n if (!closed) {\n await new Promise<void>((resolveClosed) => {\n stream.once('close', resolveClosed);\n });\n }\n\n throw error;\n } finally {\n rejectInitialization = undefined;\n initializing = false;\n }\n\n return Object.freeze({\n close() {\n if (closePromise) return closePromise;\n closing = true;\n closePromise = new Promise<void>((resolveClosed) => {\n if (closed) {\n resolveClosed();\n return;\n }\n\n stream.once('close', resolveClosed);\n try {\n if (!stream.destroyed) stream.end();\n } catch (error) {\n fail(error);\n }\n });\n\n return closePromise;\n },\n getStats: () => Object.freeze({\n droppedCount,\n pendingBytes,\n writeErrorCount,\n }),\n write(text: string) {\n if (closing || closed || failed) {\n report({\n message: 'File output is closed or unavailable.',\n type: 'closed',\n });\n\n return Promise.resolve();\n }\n\n const bytes = Buffer.byteLength(text);\n if (bytes > maxPendingBytes - pendingBytes) {\n droppedCount++;\n report({\n droppedCount,\n message: 'File output queue is full; the newest record was discarded.',\n type: 'overflow',\n });\n\n return Promise.resolve();\n }\n\n pendingBytes += bytes;\n return new Promise<void>((resolveWritten) => {\n let settled = false;\n const settle = () => {\n if (settled) return;\n settled = true;\n pendingBytes -= bytes;\n unfinished.delete(settle);\n resolveWritten();\n };\n\n unfinished.add(settle);\n try {\n stream.write(\n text,\n (error) => {\n if (error) fail(error);\n settle();\n },\n );\n } catch (error) {\n fail(error);\n }\n });\n },\n });\n}\n\n/**\n * Validate before mkdir, stream creation, or other filesystem side effects.\n */\nfunction validateFileOptions(options: LoggerFileOptions) {\n if (\n !options\n || typeof options !== 'object'\n || !(\n typeof options.path === 'string'\n || options.path instanceof Path\n || options.path instanceof URL\n || Buffer.isBuffer(options.path)\n )\n ) {\n throw new TypeError('file.path must be a string, Buffer, file URL, or Path');\n }\n\n const path = options.path instanceof URL\n ? fileURLToPath(options.path)\n : options.path.toString();\n\n if (Buffer.isBuffer(options.path) && !Buffer.from(path).equals(options.path)) {\n throw new TypeError('file.path Buffer must contain valid UTF-8');\n }\n\n if (!path.trim() || path.includes('\\0')) {\n throw new TypeError('file.path must be a nonempty path without null bytes');\n }\n\n const maxPendingBytes = options.maxPendingBytes ?? defaultMaxPendingBytes;\n if (!Number.isSafeInteger(maxPendingBytes) || maxPendingBytes <= 0) {\n throw new RangeError('file.maxPendingBytes must be a positive safe integer');\n }\n\n const rotate = options.rotate;\n if (rotate !== undefined && rotate !== false) {\n if (!rotate || typeof rotate !== 'object') {\n throw new TypeError('file.rotate must be false or an options object');\n }\n\n if (rotate.interval !== undefined && rotate.interval !== 'hourly' && rotate.interval !== 'daily') {\n throw new TypeError('file.rotate.interval must be hourly or daily');\n }\n\n if (rotate.interval === undefined && rotate.maxSize === undefined) {\n throw new TypeError('file.rotate requires interval or maxSize');\n }\n\n const keepFiles = rotate.keepFiles ?? 30;\n if (!Number.isSafeInteger(keepFiles) || keepFiles <= 0) {\n throw new RangeError('file.rotate.keepFiles must be a positive safe integer');\n }\n\n if (rotate.compress !== undefined && typeof rotate.compress !== 'boolean') {\n throw new TypeError('file.rotate.compress must be a boolean');\n }\n\n validateSize(rotate.maxSize, 'file.rotate.maxSize');\n validateSize(rotate.maxTotalSize, 'file.rotate.maxTotalSize');\n }\n\n return {\n maxPendingBytes,\n path: Path.resolve(path).value,\n rotate,\n };\n}\n\nfunction validateSize(value: string | undefined, name: string) {\n if (value === undefined) return;\n if (typeof value !== 'string' || !/^[1-9]\\d*[BKMG]$/.test(value)) {\n throw new TypeError(`${name} must be a positive integer followed by B, K, M, or G`);\n }\n\n const multiplier = {\n B: 1,\n G: 1024 ** 3,\n K: 1024,\n M: 1024 ** 2,\n }[value.at(-1) as 'B' | 'G' | 'K' | 'M'];\n\n if (!Number.isSafeInteger(Number(value.slice(0, -1)) * multiplier)) {\n throw new RangeError(`${name} exceeds the supported size range`);\n }\n}\n"],"mappings":";;;;;;;AAwBA,MAAM,yBAAyB;;;;AAO/B,eAAsB,iBAClB,SACA,QACmC;CACnC,MAAM,EACF,iBACA,MACA,WACA,oBAAoB,OAAO;CAE/B,MAAM,uBAAuB,UAAU,MAAM,OAAO,wBAAA,CAAyB,eAAe,KAAA;CAC5F,MAAM,MAAM,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,SAAmB,UAAU,uBAC7B,qBACE,SAAS,IAAI,GACb;EACI,UAAU,OAAO,aAAa,QAAQ,QAAQ;EAC9C,GAAG,OAAO,aAAa,KAAA,IAAY,CAAC,IAAI,EAAE,UAAU,OAAO,aAAa,WAAW,OAAO,KAAK;EAC/F,kBAAkB;EAClB,aAAa;EACb,UAAU,OAAO,aAAa;EAC9B,GAAG,OAAO,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,SAAS,OAAO,aAAa;EAC3E,MAAM,QAAQ,IAAI;EAClB,GAAG,OAAO,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,OAAO,QAAQ;CAClE,CACJ,IACE,kBAAkB,MAAM,EAAE,OAAO,IAAI,CAAC;CAE5C,IAAI,eAAe;CACnB,IAAI,eAAe;CACnB,IAAI,kBAAkB;CACtB,IAAI,SAAS;CACb,IAAI,UAAU;CACd,IAAI,SAAS;CACb,IAAI;CACJ,IAAI,eAAe;CACnB,MAAM,6BAAa,IAAI,IAAgB;CACvC,IAAI;CAEJ,MAAM,kBAAkB;EACpB,KAAK,MAAM,UAAU,YAAY,OAAO;CAC5C;CAEA,MAAM,QAAQ,UAAmB;EAC7B,uBAAuB,KAAK;EAC5B,IAAI,CAAC,QAAQ;GACT,SAAS;GACT,IAAI,CAAC,cAAc;IACf;IACA,OAAO;KACH;KACA,SAAS;KACT,MAAM;IACV,CAAC;GACL;EACJ;EAEA,UAAU;EACV,IAAI,CAAC,OAAO,WAAW,OAAO,QAAQ;CAC1C;CAEA,OAAO,GAAG,SAAS,IAAI;CACvB,OAAO,GACH,YACC,UAAmB;EAChB;EACA,OAAO;GACH;GACA,SAAS;GACT,MAAM;EACV,CAAC;CACL,CACJ;CAEA,OAAO,GACH,eACM;EACF,SAAS;EACT,IAAI,CAAC,WAAW,CAAC,QAAQ,qBAAK,IAAI,MAAM,kCAAkC,CAAC;EAC3E,UAAU;CACd,CACJ;CAGA,IAAI;EACA,MAAM,IAAI,SAAe,cAAc,gBAAgB;GACnD,uBAAuB;GACvB,OAAO,MACH,OAAO,MAAM,CAAC,IACb,UAAU;IACP,IAAI,OAAO,YAAY,KAAK;SACvB,aAAa;GACtB,CACJ;EACJ,CAAC;EAED,IAAI,UAAU,QAAQ,MAAM,IAAI,MAAM,2CAA2C;CACrF,SAAS,OAAO;EACZ,IAAI,CAAC,OAAO,WAAW,OAAO,QAAQ;EACtC,IAAI,CAAC,QACD,MAAM,IAAI,SAAe,kBAAkB;GACvC,OAAO,KAAK,SAAS,aAAa;EACtC,CAAC;EAGL,MAAM;CACV,UAAU;EACN,uBAAuB,KAAA;EACvB,eAAe;CACnB;CAEA,OAAO,OAAO,OAAO;EACjB,QAAQ;GACJ,IAAI,cAAc,OAAO;GACzB,UAAU;GACV,eAAe,IAAI,SAAe,kBAAkB;IAChD,IAAI,QAAQ;KACR,cAAc;KACd;IACJ;IAEA,OAAO,KAAK,SAAS,aAAa;IAClC,IAAI;KACA,IAAI,CAAC,OAAO,WAAW,OAAO,IAAI;IACtC,SAAS,OAAO;KACZ,KAAK,KAAK;IACd;GACJ,CAAC;GAED,OAAO;EACX;EACA,gBAAgB,OAAO,OAAO;GAC1B;GACA;GACA;EACJ,CAAC;EACD,MAAM,MAAc;GAChB,IAAI,WAAW,UAAU,QAAQ;IAC7B,OAAO;KACH,SAAS;KACT,MAAM;IACV,CAAC;IAED,OAAO,QAAQ,QAAQ;GAC3B;GAEA,MAAM,QAAQ,OAAO,WAAW,IAAI;GACpC,IAAI,QAAQ,kBAAkB,cAAc;IACxC;IACA,OAAO;KACH;KACA,SAAS;KACT,MAAM;IACV,CAAC;IAED,OAAO,QAAQ,QAAQ;GAC3B;GAEA,gBAAgB;GAChB,OAAO,IAAI,SAAe,mBAAmB;IACzC,IAAI,UAAU;IACd,MAAM,eAAe;KACjB,IAAI,SAAS;KACb,UAAU;KACV,gBAAgB;KAChB,WAAW,OAAO,MAAM;KACxB,eAAe;IACnB;IAEA,WAAW,IAAI,MAAM;IACrB,IAAI;KACA,OAAO,MACH,OACC,UAAU;MACP,IAAI,OAAO,KAAK,KAAK;MACrB,OAAO;KACX,CACJ;IACJ,SAAS,OAAO;KACZ,KAAK,KAAK;IACd;GACJ,CAAC;EACL;CACJ,CAAC;AACL;;;;AAKA,SAAS,oBAAoB,SAA4B;CACrD,IACI,CAAC,WACE,OAAO,YAAY,YACnB,EACC,OAAO,QAAQ,SAAS,YACrB,QAAQ,gBAAgB,QACxB,QAAQ,gBAAgB,OACxB,OAAO,SAAS,QAAQ,IAAI,IAGnC,MAAM,IAAI,UAAU,uDAAuD;CAG/E,MAAM,OAAO,QAAQ,gBAAgB,MAC/B,cAAc,QAAQ,IAAI,IAC1B,QAAQ,KAAK,SAAS;CAE5B,IAAI,OAAO,SAAS,QAAQ,IAAI,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,OAAO,QAAQ,IAAI,GACvE,MAAM,IAAI,UAAU,2CAA2C;CAGnE,IAAI,CAAC,KAAK,KAAK,KAAK,KAAK,SAAS,IAAI,GAClC,MAAM,IAAI,UAAU,sDAAsD;CAG9E,MAAM,kBAAkB,QAAQ,mBAAmB;CACnD,IAAI,CAAC,OAAO,cAAc,eAAe,KAAK,mBAAmB,GAC7D,MAAM,IAAI,WAAW,sDAAsD;CAG/E,MAAM,SAAS,QAAQ;CACvB,IAAI,WAAW,KAAA,KAAa,WAAW,OAAO;EAC1C,IAAI,CAAC,UAAU,OAAO,WAAW,UAC7B,MAAM,IAAI,UAAU,gDAAgD;EAGxE,IAAI,OAAO,aAAa,KAAA,KAAa,OAAO,aAAa,YAAY,OAAO,aAAa,SACrF,MAAM,IAAI,UAAU,8CAA8C;EAGtE,IAAI,OAAO,aAAa,KAAA,KAAa,OAAO,YAAY,KAAA,GACpD,MAAM,IAAI,UAAU,0CAA0C;EAGlE,MAAM,YAAY,OAAO,aAAa;EACtC,IAAI,CAAC,OAAO,cAAc,SAAS,KAAK,aAAa,GACjD,MAAM,IAAI,WAAW,uDAAuD;EAGhF,IAAI,OAAO,aAAa,KAAA,KAAa,OAAO,OAAO,aAAa,WAC5D,MAAM,IAAI,UAAU,wCAAwC;EAGhE,aAAa,OAAO,SAAS,qBAAqB;EAClD,aAAa,OAAO,cAAc,0BAA0B;CAChE;CAEA,OAAO;EACH;EACA,MAAM,KAAK,QAAQ,IAAI,CAAC,CAAC;EACzB;CACJ;AACJ;AAEA,SAAS,aAAa,OAA2B,MAAc;CAC3D,IAAI,UAAU,KAAA,GAAW;CACzB,IAAI,OAAO,UAAU,YAAY,CAAC,mBAAmB,KAAK,KAAK,GAC3D,MAAM,IAAI,UAAU,GAAG,KAAK,sDAAsD;CAGtF,MAAM,aAAa;EACf,GAAG;EACH,GAAG,QAAQ;EACX,GAAG;EACH,GAAG,QAAQ;CACf,EAAE,MAAM,GAAG,EAAE;CAEb,IAAI,CAAC,OAAO,cAAc,OAAO,MAAM,MAAM,GAAG,EAAE,CAAC,IAAI,UAAU,GAC7D,MAAM,IAAI,WAAW,GAAG,KAAK,kCAAkC;AAEvE"}
|
|
1
|
+
{"version":3,"file":"file-writer.js","names":[],"sources":["../../../../src/logger/node/_internals/file-writer.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { createWriteStream } from 'node:fs';\nimport { mkdir } from 'node:fs/promises';\nimport {\n basename,\n dirname,\n} from 'node:path';\nimport type { Writable } from 'node:stream';\nimport { fileURLToPath } from 'node:url';\n\nimport { Path } from '../../../classes/path';\nimport type { LoggerDiagnostic } from '../../types';\nimport type {\n LoggerFileOptions,\n NodeLoggerStats,\n} from '../types';\n\ninterface LoggerFileWriter {\n close: () => Promise<void>;\n getStats: () => Readonly<NodeLoggerStats>;\n write: (text: string) => Promise<void>;\n}\n\n// Constants/Variables\nconst defaultMaxPendingBytes = 8 * 1024 * 1024;\n\n// Functions\n\n/**\n * A bounded admission layer over the stream's own ordered write buffer; not a second FIFO.\n */\nexport async function createFileWriter(\n options: LoggerFileOptions,\n report: (event: LoggerDiagnostic) => void,\n): Promise<Readonly<LoggerFileWriter>> {\n const {\n maxPendingBytes,\n path,\n rotate,\n } = validateFileOptions(options);\n\n const createRotatingStream = rotate ? (await import('rotating-file-stream')).createStream : undefined;\n await mkdir(dirname(path), { recursive: true });\n const stream: Writable = rotate && createRotatingStream\n ? createRotatingStream(\n basename(path),\n {\n compress: rotate.compress === false ? false : 'gzip',\n ...rotate.interval === undefined ? {} : { interval: rotate.interval === 'hourly' ? '1h' : '1d' },\n intervalBoundary: true,\n intervalUTC: false,\n maxFiles: rotate.keepFiles ?? 30,\n ...rotate.maxTotalSize === undefined ? {} : { maxSize: rotate.maxTotalSize },\n path: dirname(path),\n ...rotate.maxSize === undefined ? {} : { size: rotate.maxSize },\n },\n )\n : createWriteStream(path, { flags: 'a' });\n\n let pendingBytes = 0;\n let droppedCount = 0;\n let writeErrorCount = 0;\n let failed = false;\n let closing = false;\n let closed = false;\n let closePromise: Promise<void> | undefined;\n let initializing = true;\n const unfinished = new Set<() => void>();\n let rejectInitialization: ((error: unknown) => void) | undefined;\n\n const settleAll = () => {\n for (const settle of unfinished) settle();\n };\n\n const fail = (error: unknown) => {\n rejectInitialization?.(error);\n if (!failed) {\n failed = true;\n if (!initializing) {\n writeErrorCount++;\n report({\n error,\n message: 'File output failed and has been disabled.',\n type: 'file',\n });\n }\n }\n\n settleAll();\n if (!stream.destroyed) stream.destroy();\n };\n\n stream.on('error', fail);\n stream.on(\n 'warning',\n (error: unknown) => {\n writeErrorCount++;\n report({\n error,\n message: 'File rotation cleanup failed.',\n type: 'file',\n });\n },\n );\n\n stream.on(\n 'close',\n () => {\n closed = true;\n if (!closing && !failed) fail(new Error('File stream closed unexpectedly.'));\n settleAll();\n },\n );\n\n // A write callback also waits for RFS's asynchronous initialization and initial rotation.\n try {\n await new Promise<void>((resolveReady, rejectReady) => {\n rejectInitialization = rejectReady;\n stream.write(\n Buffer.alloc(0),\n (error) => {\n if (error) rejectReady(error);\n else resolveReady();\n },\n );\n });\n\n if (failed || closed) throw new Error('File output failed during initialization.');\n } catch (error) {\n if (!stream.destroyed) stream.destroy();\n if (!closed) {\n await new Promise<void>((resolveClosed) => {\n stream.once('close', resolveClosed);\n });\n }\n\n throw error;\n } finally {\n rejectInitialization = undefined;\n initializing = false;\n }\n\n return Object.freeze({\n async close() {\n if (closePromise) return await closePromise;\n closing = true;\n closePromise = new Promise<void>((resolveClosed) => {\n if (closed) {\n resolveClosed();\n return;\n }\n\n stream.once('close', resolveClosed);\n try {\n if (!stream.destroyed) stream.end();\n } catch (error) {\n fail(error);\n }\n });\n\n return await closePromise;\n },\n getStats: () => Object.freeze({\n droppedCount,\n pendingBytes,\n writeErrorCount,\n }),\n async write(text: string) {\n if (closing || closed || failed) {\n report({\n message: 'File output is closed or unavailable.',\n type: 'closed',\n });\n\n return await Promise.resolve();\n }\n\n const bytes = Buffer.byteLength(text);\n if (bytes > maxPendingBytes - pendingBytes) {\n droppedCount++;\n report({\n droppedCount,\n message: 'File output queue is full; the newest record was discarded.',\n type: 'overflow',\n });\n\n return await Promise.resolve();\n }\n\n pendingBytes += bytes;\n return await new Promise<void>((resolveWritten) => {\n let settled = false;\n const settle = () => {\n if (settled) return;\n settled = true;\n pendingBytes -= bytes;\n unfinished.delete(settle);\n resolveWritten();\n };\n\n unfinished.add(settle);\n try {\n stream.write(\n text,\n (error) => {\n if (error) fail(error);\n settle();\n },\n );\n } catch (error) {\n fail(error);\n }\n });\n },\n });\n}\n\n/**\n * Validate before mkdir, stream creation, or other filesystem side effects.\n */\nfunction validateFileOptions(options: LoggerFileOptions) {\n if (\n !options\n || typeof options !== 'object'\n || !(\n typeof options.path === 'string'\n || options.path instanceof Path\n || options.path instanceof URL\n || Buffer.isBuffer(options.path)\n )\n ) {\n throw new TypeError('file.path must be a string, Buffer, file URL, or Path');\n }\n\n const path = options.path instanceof URL\n ? fileURLToPath(options.path)\n : options.path.toString();\n\n if (Buffer.isBuffer(options.path) && !Buffer.from(path).equals(options.path)) {\n throw new TypeError('file.path Buffer must contain valid UTF-8');\n }\n\n if (!path.trim() || path.includes('\\0')) {\n throw new TypeError('file.path must be a nonempty path without null bytes');\n }\n\n const maxPendingBytes = options.maxPendingBytes ?? defaultMaxPendingBytes;\n if (!Number.isSafeInteger(maxPendingBytes) || maxPendingBytes <= 0) {\n throw new RangeError('file.maxPendingBytes must be a positive safe integer');\n }\n\n const rotate = options.rotate;\n if (rotate !== undefined && rotate !== false) {\n if (!rotate || typeof rotate !== 'object') {\n throw new TypeError('file.rotate must be false or an options object');\n }\n\n if (rotate.interval !== undefined && rotate.interval !== 'hourly' && rotate.interval !== 'daily') {\n throw new TypeError('file.rotate.interval must be hourly or daily');\n }\n\n if (rotate.interval === undefined && rotate.maxSize === undefined) {\n throw new TypeError('file.rotate requires interval or maxSize');\n }\n\n const keepFiles = rotate.keepFiles ?? 30;\n if (!Number.isSafeInteger(keepFiles) || keepFiles <= 0) {\n throw new RangeError('file.rotate.keepFiles must be a positive safe integer');\n }\n\n if (rotate.compress !== undefined && typeof rotate.compress !== 'boolean') {\n throw new TypeError('file.rotate.compress must be a boolean');\n }\n\n validateSize(rotate.maxSize, 'file.rotate.maxSize');\n validateSize(rotate.maxTotalSize, 'file.rotate.maxTotalSize');\n }\n\n return {\n maxPendingBytes,\n path: Path.resolve(path).value,\n rotate,\n };\n}\n\nfunction validateSize(value: string | undefined, name: string) {\n if (value === undefined) return;\n if (typeof value !== 'string' || !/^[1-9]\\d*[BKMG]$/.test(value)) {\n throw new TypeError(`${name} must be a positive integer followed by B, K, M, or G`);\n }\n\n const multiplier = {\n B: 1,\n G: 1024 ** 3,\n K: 1024,\n M: 1024 ** 2,\n }[value.at(-1) as 'B' | 'G' | 'K' | 'M'];\n\n if (!Number.isSafeInteger(Number(value.slice(0, -1)) * multiplier)) {\n throw new RangeError(`${name} exceeds the supported size range`);\n }\n}\n"],"mappings":"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"}
|
|
@@ -24,7 +24,7 @@ async function createNodeLogger(options = {}) {
|
|
|
24
24
|
const consoleWrites = /* @__PURE__ */ new Set();
|
|
25
25
|
const logger = createLoggerCore(options, {
|
|
26
26
|
report,
|
|
27
|
-
write(record) {
|
|
27
|
+
async write(record) {
|
|
28
28
|
if (closed) {
|
|
29
29
|
report({
|
|
30
30
|
message: "Logger is closed.",
|
|
@@ -42,16 +42,16 @@ async function createNodeLogger(options = {}) {
|
|
|
42
42
|
consoleWrites.add(completion);
|
|
43
43
|
completion.then(() => consoleWrites.delete(completion));
|
|
44
44
|
const fileCompletion = writer?.write(`${stripVTControlCharacters(text)}\n`);
|
|
45
|
-
return fileCompletion ? Promise.all([completion, fileCompletion]).then(() => {}) : completion;
|
|
45
|
+
return fileCompletion ? await Promise.all([completion, fileCompletion]).then(() => {}) : await completion;
|
|
46
46
|
}
|
|
47
|
-
return writer?.write(`${stripVTControlCharacters(text)}\n`);
|
|
47
|
+
return await writer?.write(`${stripVTControlCharacters(text)}\n`);
|
|
48
48
|
}
|
|
49
49
|
});
|
|
50
50
|
if (options.file !== void 0 && options.file !== false) writer = await createFileWriter(options.file, report);
|
|
51
51
|
return Object.freeze({
|
|
52
52
|
...logger,
|
|
53
|
-
close() {
|
|
54
|
-
if (closePromise) return closePromise;
|
|
53
|
+
async close() {
|
|
54
|
+
if (closePromise) return await closePromise;
|
|
55
55
|
closed = true;
|
|
56
56
|
closePromise = (async () => {
|
|
57
57
|
await Promise.all([writer?.close(), ...consoleWrites]);
|
|
@@ -67,7 +67,7 @@ async function createNodeLogger(options = {}) {
|
|
|
67
67
|
}
|
|
68
68
|
consoleTargets.clear();
|
|
69
69
|
})();
|
|
70
|
-
return closePromise;
|
|
70
|
+
return await closePromise;
|
|
71
71
|
},
|
|
72
72
|
getStats: () => writer?.getStats() ?? Object.freeze({
|
|
73
73
|
droppedCount: 0,
|
|
@@ -79,7 +79,7 @@ async function createNodeLogger(options = {}) {
|
|
|
79
79
|
/**
|
|
80
80
|
* One error listener per shared output stream; retained until its last owning logger calls close().
|
|
81
81
|
*/
|
|
82
|
-
function writeConsole(target, text, report, owner) {
|
|
82
|
+
async function writeConsole(target, text, report, owner) {
|
|
83
83
|
let state = consoleStreams.get(target);
|
|
84
84
|
if (!state) {
|
|
85
85
|
const pending = /* @__PURE__ */ new Set();
|
|
@@ -99,7 +99,7 @@ function writeConsole(target, text, report, owner) {
|
|
|
99
99
|
}
|
|
100
100
|
state.owners.add(owner);
|
|
101
101
|
const current = state;
|
|
102
|
-
return new Promise((resolveWritten) => {
|
|
102
|
+
return await new Promise((resolveWritten) => {
|
|
103
103
|
let settled = false;
|
|
104
104
|
const settle = (error) => {
|
|
105
105
|
if (settled) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/logger/node/index.ts"],"sourcesContent":["import { writeSync } from 'node:fs';\nimport { stripVTControlCharacters } from 'node:util';\n\nimport { supportsLoggerColor } from '../_internals/formatter';\nimport { getLoggerStreamName } from '../_internals/levels';\nimport {\n createDiagnosticReporter,\n createLoggerCore,\n} from '../_internals/logger';\nimport type { LoggerDiagnostic } from '../types';\n\nimport { createFileWriter } from './_internals/file-writer';\nimport { formatNodeLoggerRecord } from './_internals/formatter';\nimport type {\n NodeLogger,\n NodeLoggerOptions,\n} from './types';\n\ninterface ConsoleStreamState {\n onClose: () => void;\n onError: (error: Error) => void;\n owners: Set<object>;\n pending: Set<(error?: Error | null) => void>;\n}\n\n// Constants/Variables\nconst consoleStreams = new WeakMap<NodeJS.WriteStream, ConsoleStreamState>();\n\n// Functions\n\n/**\n * Creates a Node.js/Bun logger with optional bounded, append-only file output and rotation.\n *\n * Log calls never throw or reject. Awaiting them waits for processing, not proof of successful\n * persistence: overload drops the newest file record and failures are reported safely. Factory\n * validation/open failures reject. Call close explicitly; no process signal handlers are installed.\n */\nexport async function createNodeLogger(options: NodeLoggerOptions = {}): Promise<Readonly<NodeLogger>> {\n const report = createDiagnosticReporter(options.onError, (message) => writeSync(2, message));\n let writer: Awaited<ReturnType<typeof createFileWriter>> | undefined;\n let closed = false;\n let closePromise: Promise<void> | undefined;\n const consoleOwner = {};\n const consoleTargets = new Set<NodeJS.WriteStream>();\n const consoleWrites = new Set<Promise<void>>();\n const logger = createLoggerCore(\n options,\n {\n report,\n write(record) {\n if (closed) {\n report({\n message: 'Logger is closed.',\n type: 'closed',\n });\n\n return;\n }\n\n if (!record.console && !writer) return;\n const target = process[getLoggerStreamName(record.type)];\n\n const text = formatNodeLoggerRecord(record, record.console && supportsLoggerColor(target));\n if (record.console) {\n const line = `${text}\\n`;\n consoleTargets.add(target);\n const completion = writeConsole(target, line, report, consoleOwner);\n consoleWrites.add(completion);\n void completion.then(() => consoleWrites.delete(completion));\n const fileCompletion = writer?.write(`${stripVTControlCharacters(text)}\\n`);\n return fileCompletion\n ? Promise.all([\n completion,\n fileCompletion,\n ]).then(() => {})\n : completion;\n }\n\n return writer?.write(`${stripVTControlCharacters(text)}\\n`);\n },\n },\n );\n\n if (options.file !== undefined && options.file !== false) writer = await createFileWriter(options.file, report);\n\n return Object.freeze({\n ...logger,\n close() {\n if (closePromise) return closePromise;\n closed = true;\n closePromise = (async () => {\n await Promise.all([\n writer?.close(),\n ...consoleWrites,\n ]);\n\n for (const target of consoleTargets) {\n const state = consoleStreams.get(target);\n if (!state) continue;\n state.owners.delete(consoleOwner);\n if (!state.owners.size) {\n target.off('error', state.onError);\n target.off('close', state.onClose);\n consoleStreams.delete(target);\n }\n }\n\n consoleTargets.clear();\n })();\n\n return closePromise;\n },\n getStats: () => writer?.getStats() ?? Object.freeze({\n droppedCount: 0,\n pendingBytes: 0,\n writeErrorCount: 0,\n }),\n });\n}\n\n/**\n * One error listener per shared output stream; retained until its last owning logger calls close().\n */\
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/logger/node/index.ts"],"sourcesContent":["import { writeSync } from 'node:fs';\nimport { stripVTControlCharacters } from 'node:util';\n\nimport { supportsLoggerColor } from '../_internals/formatter';\nimport { getLoggerStreamName } from '../_internals/levels';\nimport {\n createDiagnosticReporter,\n createLoggerCore,\n} from '../_internals/logger';\nimport type { LoggerDiagnostic } from '../types';\n\nimport { createFileWriter } from './_internals/file-writer';\nimport { formatNodeLoggerRecord } from './_internals/formatter';\nimport type {\n NodeLogger,\n NodeLoggerOptions,\n} from './types';\n\ninterface ConsoleStreamState {\n onClose: () => void;\n onError: (error: Error) => void;\n owners: Set<object>;\n pending: Set<(error?: Error | null) => void>;\n}\n\n// Constants/Variables\nconst consoleStreams = new WeakMap<NodeJS.WriteStream, ConsoleStreamState>();\n\n// Functions\n\n/**\n * Creates a Node.js/Bun logger with optional bounded, append-only file output and rotation.\n *\n * Log calls never throw or reject. Awaiting them waits for processing, not proof of successful\n * persistence: overload drops the newest file record and failures are reported safely. Factory\n * validation/open failures reject. Call close explicitly; no process signal handlers are installed.\n */\nexport async function createNodeLogger(options: NodeLoggerOptions = {}): Promise<Readonly<NodeLogger>> {\n const report = createDiagnosticReporter(options.onError, (message) => writeSync(2, message));\n let writer: Awaited<ReturnType<typeof createFileWriter>> | undefined;\n let closed = false;\n let closePromise: Promise<void> | undefined;\n const consoleOwner = {};\n const consoleTargets = new Set<NodeJS.WriteStream>();\n const consoleWrites = new Set<Promise<void>>();\n const logger = createLoggerCore(\n options,\n {\n report,\n async write(record) {\n if (closed) {\n report({\n message: 'Logger is closed.',\n type: 'closed',\n });\n\n return;\n }\n\n if (!record.console && !writer) return;\n const target = process[getLoggerStreamName(record.type)];\n\n const text = formatNodeLoggerRecord(record, record.console && supportsLoggerColor(target));\n if (record.console) {\n const line = `${text}\\n`;\n consoleTargets.add(target);\n const completion = writeConsole(target, line, report, consoleOwner);\n consoleWrites.add(completion);\n void completion.then(() => consoleWrites.delete(completion));\n const fileCompletion = writer?.write(`${stripVTControlCharacters(text)}\\n`);\n return fileCompletion\n ? await Promise.all([\n completion,\n fileCompletion,\n ]).then(() => {})\n : await completion;\n }\n\n return await writer?.write(`${stripVTControlCharacters(text)}\\n`);\n },\n },\n );\n\n if (options.file !== undefined && options.file !== false) writer = await createFileWriter(options.file, report);\n\n return Object.freeze({\n ...logger,\n async close() {\n if (closePromise) return await closePromise;\n closed = true;\n closePromise = (async () => {\n await Promise.all([\n writer?.close(),\n ...consoleWrites,\n ]);\n\n for (const target of consoleTargets) {\n const state = consoleStreams.get(target);\n if (!state) continue;\n state.owners.delete(consoleOwner);\n if (!state.owners.size) {\n target.off('error', state.onError);\n target.off('close', state.onClose);\n consoleStreams.delete(target);\n }\n }\n\n consoleTargets.clear();\n })();\n\n return await closePromise;\n },\n getStats: () => writer?.getStats() ?? Object.freeze({\n droppedCount: 0,\n pendingBytes: 0,\n writeErrorCount: 0,\n }),\n });\n}\n\n/**\n * One error listener per shared output stream; retained until its last owning logger calls close().\n */\nasync function writeConsole(\n target: NodeJS.WriteStream,\n text: string,\n report: (event: LoggerDiagnostic) => void,\n owner: object,\n) {\n let state = consoleStreams.get(target);\n if (!state) {\n const pending = new Set<(error?: Error | null) => void>();\n const onError = (error: Error) => {\n for (const settle of pending) settle(error);\n };\n\n const onClose = () => onError(new Error('Console stream closed.'));\n state = {\n onClose,\n onError,\n owners: new Set(),\n pending,\n };\n\n consoleStreams.set(target, state);\n target.on('error', onError);\n target.on('close', onClose);\n }\n\n state.owners.add(owner);\n const current = state;\n return await new Promise<void>((resolveWritten) => {\n let settled = false;\n const settle = (error?: Error | null) => {\n if (settled) return;\n settled = true;\n current.pending.delete(settle);\n if (error) {\n report({\n error,\n message: 'Console output failed.',\n type: 'console',\n });\n }\n\n resolveWritten();\n };\n\n current.pending.add(settle);\n try {\n target.write(text, settle);\n } catch (error) {\n settle(error instanceof Error ? error : new Error('Console output failed.'));\n }\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyed-store.d.ts","names":[],"sources":["../../../src/storages/redis/keyed-store.ts"],"mappings":";;;;;;;;;;;;;;;;;;wBAoBgB,sBAAsB,aAAa,SAAS,cAAc,qBAC9D,iBAAiB,WAAW,MAAM,iBAAY;
|
|
1
|
+
{"version":3,"file":"keyed-store.d.ts","names":[],"sources":["../../../src/storages/redis/keyed-store.ts"],"mappings":";;;;;;;;;;;;;;;;;;wBAoBgB,sBAAsB,aAAa,SAAS,cAAc,qBAC9D,iBAAiB,WAAW,MAAM,iBAAY;EAChB,sBAAA,MAAA,MAAC,QAAA;EACV,aAAA,MAAA,MAAC,QAAA;EACE,gBAAA,MAAA,MAAC;EACJ,aAAA,MAAA,MAAC;EACE,gBAAA,MAAA,MAAC;EACP,gBAAA,MAAA;EACC,UAAA,OAAA,MAAC,MAAW,MAAC;EACH,eAAA,oBAAM,OAAS,MAAC,MAAW,IAAC;EAGpB,uBAAA,oBAAM,OAAS,MAAC,MAAW,IAAC"}
|
|
@@ -17,18 +17,18 @@
|
|
|
17
17
|
*/
|
|
18
18
|
function createRedisKeyedStore(storage) {
|
|
19
19
|
return (keyFn) => Object.freeze({
|
|
20
|
-
getAndRemoveItem: (...args) => storage.getAndRemoveItem(keyFn(...args)),
|
|
21
|
-
getItem: (...args) => storage.getItem(keyFn(...args)),
|
|
22
|
-
getItemTtl: (...args) => storage.getItemTtl(keyFn(...args)),
|
|
23
|
-
hasItem: (...args) => storage.hasItem(keyFn(...args)),
|
|
24
|
-
removeItem: (...args) => storage.removeItem(keyFn(...args)),
|
|
20
|
+
getAndRemoveItem: async (...args) => await storage.getAndRemoveItem(keyFn(...args)),
|
|
21
|
+
getItem: async (...args) => await storage.getItem(keyFn(...args)),
|
|
22
|
+
getItemTtl: async (...args) => await storage.getItemTtl(keyFn(...args)),
|
|
23
|
+
hasItem: async (...args) => await storage.hasItem(keyFn(...args)),
|
|
24
|
+
removeItem: async (...args) => await storage.removeItem(keyFn(...args)),
|
|
25
25
|
resolveKey: (...args) => keyFn(...args),
|
|
26
|
-
setItem: (value, ...args) => storage.setItem(keyFn(...args), value),
|
|
27
|
-
setItemWithTtl(ttlSeconds, value, ...args) {
|
|
28
|
-
return storage.setItemWithTtl(keyFn(...args), ttlSeconds, value);
|
|
26
|
+
setItem: async (value, ...args) => await storage.setItem(keyFn(...args), value),
|
|
27
|
+
async setItemWithTtl(ttlSeconds, value, ...args) {
|
|
28
|
+
return await storage.setItemWithTtl(keyFn(...args), ttlSeconds, value);
|
|
29
29
|
},
|
|
30
|
-
setItemWithTtlIfAbsent(ttlSeconds, value, ...args) {
|
|
31
|
-
return storage.setItemWithTtlIfAbsent(keyFn(...args), ttlSeconds, value);
|
|
30
|
+
async setItemWithTtlIfAbsent(ttlSeconds, value, ...args) {
|
|
31
|
+
return await storage.setItemWithTtlIfAbsent(keyFn(...args), ttlSeconds, value);
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keyed-store.js","names":[],"sources":["../../../src/storages/redis/keyed-store.ts"],"sourcesContent":["import type { MaybeReadonly } from '../../types';\n\nimport type { RedisLikeStorage } from './types';\n\n/**\n * Creates a keyed store factory sharing a caller-owned Redis-like storage facade.\n *\n * @remarks\n * The key resolver is called synchronously once per operation with the operation's key arguments.\n * Resolver exceptions propagate synchronously; asynchronous storage failures retain the underlying rejection.\n * `setItemWithTtl` and `setItemWithTtlIfAbsent` accept their TTL in seconds before the value and key arguments.\n * The factory does not open or close the underlying connection; cleanup remains the caller's responsibility.\n *\n * @typeParam D - The stored value type used by reads and writes.\n *\n * @param storage - The shared Redis-like storage instance.\n *\n * @returns A factory accepting a key resolver and returning a new frozen keyed facade sharing the storage.\n * Reads, atomic read-and-removals, writes, removals, and TTL results retain the underlying storage contract.\n */\nexport function createRedisKeyedStore<D = unknown>(storage: MaybeReadonly<RedisLikeStorage>) {\n return <P extends any[]>(keyFn: (...args: P) => string) => Object.freeze({\n getAndRemoveItem: (...args: P) => storage.getAndRemoveItem<D>(keyFn(...args)),\n getItem: (...args: P) => storage.getItem<D>(keyFn(...args)),\n getItemTtl: (...args: P) => storage.getItemTtl(keyFn(...args)),\n hasItem: (...args: P) => storage.hasItem(keyFn(...args)),\n removeItem: (...args: P) => storage.removeItem(keyFn(...args)),\n resolveKey: (...args: P) => keyFn(...args),\n setItem: (value: D, ...args: P) => storage.setItem(keyFn(...args), value),\n setItemWithTtl(ttlSeconds: number, value: D, ...args: P) {\n return storage.setItemWithTtl(keyFn(...args), ttlSeconds, value);\n },\n setItemWithTtlIfAbsent(ttlSeconds: number, value: D, ...args: P) {\n return storage.setItemWithTtlIfAbsent(keyFn(...args), ttlSeconds, value);\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,SAAgB,sBAAmC,SAA0C;CACzF,QAAyB,UAAkC,OAAO,OAAO;EACrE,
|
|
1
|
+
{"version":3,"file":"keyed-store.js","names":[],"sources":["../../../src/storages/redis/keyed-store.ts"],"sourcesContent":["import type { MaybeReadonly } from '../../types';\n\nimport type { RedisLikeStorage } from './types';\n\n/**\n * Creates a keyed store factory sharing a caller-owned Redis-like storage facade.\n *\n * @remarks\n * The key resolver is called synchronously once per operation with the operation's key arguments.\n * Resolver exceptions propagate synchronously; asynchronous storage failures retain the underlying rejection.\n * `setItemWithTtl` and `setItemWithTtlIfAbsent` accept their TTL in seconds before the value and key arguments.\n * The factory does not open or close the underlying connection; cleanup remains the caller's responsibility.\n *\n * @typeParam D - The stored value type used by reads and writes.\n *\n * @param storage - The shared Redis-like storage instance.\n *\n * @returns A factory accepting a key resolver and returning a new frozen keyed facade sharing the storage.\n * Reads, atomic read-and-removals, writes, removals, and TTL results retain the underlying storage contract.\n */\nexport function createRedisKeyedStore<D = unknown>(storage: MaybeReadonly<RedisLikeStorage>) {\n return <P extends any[]>(keyFn: (...args: P) => string) => Object.freeze({\n getAndRemoveItem: async (...args: P) => await storage.getAndRemoveItem<D>(keyFn(...args)),\n getItem: async (...args: P) => await storage.getItem<D>(keyFn(...args)),\n getItemTtl: async (...args: P) => await storage.getItemTtl(keyFn(...args)),\n hasItem: async (...args: P) => await storage.hasItem(keyFn(...args)),\n removeItem: async (...args: P) => await storage.removeItem(keyFn(...args)),\n resolveKey: (...args: P) => keyFn(...args),\n setItem: async (value: D, ...args: P) => await storage.setItem(keyFn(...args), value),\n async setItemWithTtl(ttlSeconds: number, value: D, ...args: P) {\n return await storage.setItemWithTtl(keyFn(...args), ttlSeconds, value);\n },\n async setItemWithTtlIfAbsent(ttlSeconds: number, value: D, ...args: P) {\n return await storage.setItemWithTtlIfAbsent(keyFn(...args), ttlSeconds, value);\n },\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,SAAgB,sBAAmC,SAA0C;CACzF,QAAyB,UAAkC,OAAO,OAAO;EACrE,kBAAkB,OAAO,GAAG,SAAY,MAAM,QAAQ,iBAAoB,MAAM,GAAG,IAAI,CAAC;EACxF,SAAS,OAAO,GAAG,SAAY,MAAM,QAAQ,QAAW,MAAM,GAAG,IAAI,CAAC;EACtE,YAAY,OAAO,GAAG,SAAY,MAAM,QAAQ,WAAW,MAAM,GAAG,IAAI,CAAC;EACzE,SAAS,OAAO,GAAG,SAAY,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,CAAC;EACnE,YAAY,OAAO,GAAG,SAAY,MAAM,QAAQ,WAAW,MAAM,GAAG,IAAI,CAAC;EACzE,aAAa,GAAG,SAAY,MAAM,GAAG,IAAI;EACzC,SAAS,OAAO,OAAU,GAAG,SAAY,MAAM,QAAQ,QAAQ,MAAM,GAAG,IAAI,GAAG,KAAK;EACpF,MAAM,eAAe,YAAoB,OAAU,GAAG,MAAS;GAC3D,OAAO,MAAM,QAAQ,eAAe,MAAM,GAAG,IAAI,GAAG,YAAY,KAAK;EACzE;EACA,MAAM,uBAAuB,YAAoB,OAAU,GAAG,MAAS;GACnE,OAAO,MAAM,QAAQ,uBAAuB,MAAM,GAAG,IAAI,GAAG,YAAY,KAAK;EACjF;CACJ,CAAC;AACL"}
|
|
@@ -61,8 +61,8 @@ function createRedisMsgpackStorage(adapter) {
|
|
|
61
61
|
const rawValue = await adapter.getBuffer(key);
|
|
62
62
|
return rawValue ? decode(rawValue) : null;
|
|
63
63
|
},
|
|
64
|
-
getItemTtl: (key) => adapter.ttl(key),
|
|
65
|
-
hasItem: (key) => adapter.has(key),
|
|
64
|
+
getItemTtl: async (key) => await adapter.ttl(key),
|
|
65
|
+
hasItem: async (key) => await adapter.has(key),
|
|
66
66
|
removeItem: async (key) => await adapter.delete(key) === 1,
|
|
67
67
|
setItem: async (key, value) => await adapter.setBuffer(key, encode(value)) === "OK",
|
|
68
68
|
async setItemWithTtl(key, ttlSeconds, value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"msgpack.js","names":[],"sources":["../../../src/storages/redis/msgpack.ts"],"sourcesContent":["import {\n decode,\n encode,\n isNativeAccelerationEnabled,\n} from 'msgpackr';\n\nimport type {\n RedisLikeAdapter,\n RedisLikeStorage,\n} from './types';\n\nif (!isNativeAccelerationEnabled) {\n console.warn('Native acceleration not enabled for msgpackr, verify that install finished properly');\n}\n\n/**\n * Creates a frozen Redis-like storage facade using MessagePack serialization.\n *\n * @remarks\n * Adapter rejections and MessagePack serialization errors propagate to the caller.\n * Reads resolve to `null` when no raw value exists; `getAndRemoveItem` reads and deletes in one atomic adapter call.\n * Writes resolve to `true` only when the adapter returns `OK`;\n * removal resolves to `true` only when exactly one key is deleted.\n * The adapter remains caller-owned; creating the facade does not open or close a connection.\n *\n * @param adapter - The caller-owned adapter for raw binary operations.\n *\n * @returns A new frozen facade sharing the supplied adapter.\n *\n * @example\n *\n * ```ts\n * import { createRedisMsgpackStorage } from '@kikiutils/shared/storages/redis/msgpack';\n * import type { RedisLikeAdapter } from '@kikiutils/shared/storages/redis/types';\n *\n * // This in-memory adapter demonstrates non-expiring operations without an external service.\n * const values = new Map<string, Uint8Array>();\n * const adapter: RedisLikeAdapter = {\n * delete: async (key) => values.delete(key) ? 1 : 0,\n * getBuffer: async (key) => values.get(key) ?? null,\n * getBufferAndDelete: async (key) => {\n * const value = values.get(key) ?? null;\n * values.delete(key);\n * return value;\n * },\n * has: async (key) => values.has(key),\n * setBuffer: async (key, value) => {\n * values.set(key, value);\n * return 'OK';\n * },\n * setBufferEx: async () => {\n * throw new Error('TTL writes are not supported by this example adapter');\n * },\n * setBufferExNx: async () => {\n * throw new Error('TTL writes are not supported by this example adapter');\n * },\n * ttl: async (key) => values.has(key) ? -1 : -2,\n * };\n *\n * const storage = createRedisMsgpackStorage(adapter);\n * await storage.setItem('profile', { name: 'Alice' });\n * const profile = await storage.getItem<{ name: string }>('profile');\n * console.log(profile); // => { name: 'Alice' }\n * ```\n */\nexport function createRedisMsgpackStorage(adapter: RedisLikeAdapter): Readonly<RedisLikeStorage> {\n return Object.freeze({\n async getAndRemoveItem<T = unknown>(key: string) {\n const rawValue = await adapter.getBufferAndDelete(key);\n return rawValue ? decode(rawValue) as T : null;\n },\n async getItem<T = unknown>(key: string) {\n const rawValue = await adapter.getBuffer(key);\n return rawValue ? decode(rawValue) as T : null;\n },\n getItemTtl: (key: string) => adapter.ttl(key),\n hasItem: (key: string) => adapter.has(key),\n removeItem: async (key: string) => await adapter.delete(key) === 1,\n setItem: async (key: string, value: any) => await adapter.setBuffer(key, encode(value)) === 'OK',\n async setItemWithTtl(key: string, ttlSeconds: number, value: any) {\n return await adapter.setBufferEx(key, ttlSeconds, encode(value)) === 'OK';\n },\n async setItemWithTtlIfAbsent(key: string, ttlSeconds: number, value: any) {\n return await adapter.setBufferExNx(key, ttlSeconds, encode(value)) === 'OK';\n },\n });\n}\n"],"mappings":";;AAWA,IAAI,CAAC,6BACD,QAAQ,KAAK,qFAAqF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDtG,SAAgB,0BAA0B,SAAuD;CAC7F,OAAO,OAAO,OAAO;EACjB,MAAM,iBAA8B,KAAa;GAC7C,MAAM,WAAW,MAAM,QAAQ,mBAAmB,GAAG;GACrD,OAAO,WAAW,OAAO,QAAQ,IAAS;EAC9C;EACA,MAAM,QAAqB,KAAa;GACpC,MAAM,WAAW,MAAM,QAAQ,UAAU,GAAG;GAC5C,OAAO,WAAW,OAAO,QAAQ,IAAS;EAC9C;EACA,
|
|
1
|
+
{"version":3,"file":"msgpack.js","names":[],"sources":["../../../src/storages/redis/msgpack.ts"],"sourcesContent":["import {\n decode,\n encode,\n isNativeAccelerationEnabled,\n} from 'msgpackr';\n\nimport type {\n RedisLikeAdapter,\n RedisLikeStorage,\n} from './types';\n\nif (!isNativeAccelerationEnabled) {\n console.warn('Native acceleration not enabled for msgpackr, verify that install finished properly');\n}\n\n/**\n * Creates a frozen Redis-like storage facade using MessagePack serialization.\n *\n * @remarks\n * Adapter rejections and MessagePack serialization errors propagate to the caller.\n * Reads resolve to `null` when no raw value exists; `getAndRemoveItem` reads and deletes in one atomic adapter call.\n * Writes resolve to `true` only when the adapter returns `OK`;\n * removal resolves to `true` only when exactly one key is deleted.\n * The adapter remains caller-owned; creating the facade does not open or close a connection.\n *\n * @param adapter - The caller-owned adapter for raw binary operations.\n *\n * @returns A new frozen facade sharing the supplied adapter.\n *\n * @example\n *\n * ```ts\n * import { createRedisMsgpackStorage } from '@kikiutils/shared/storages/redis/msgpack';\n * import type { RedisLikeAdapter } from '@kikiutils/shared/storages/redis/types';\n *\n * // This in-memory adapter demonstrates non-expiring operations without an external service.\n * const values = new Map<string, Uint8Array>();\n * const adapter: RedisLikeAdapter = {\n * delete: async (key) => values.delete(key) ? 1 : 0,\n * getBuffer: async (key) => values.get(key) ?? null,\n * getBufferAndDelete: async (key) => {\n * const value = values.get(key) ?? null;\n * values.delete(key);\n * return value;\n * },\n * has: async (key) => values.has(key),\n * setBuffer: async (key, value) => {\n * values.set(key, value);\n * return 'OK';\n * },\n * setBufferEx: async () => {\n * throw new Error('TTL writes are not supported by this example adapter');\n * },\n * setBufferExNx: async () => {\n * throw new Error('TTL writes are not supported by this example adapter');\n * },\n * ttl: async (key) => values.has(key) ? -1 : -2,\n * };\n *\n * const storage = createRedisMsgpackStorage(adapter);\n * await storage.setItem('profile', { name: 'Alice' });\n * const profile = await storage.getItem<{ name: string }>('profile');\n * console.log(profile); // => { name: 'Alice' }\n * ```\n */\nexport function createRedisMsgpackStorage(adapter: RedisLikeAdapter): Readonly<RedisLikeStorage> {\n return Object.freeze({\n async getAndRemoveItem<T = unknown>(key: string) {\n const rawValue = await adapter.getBufferAndDelete(key);\n return rawValue ? decode(rawValue) as T : null;\n },\n async getItem<T = unknown>(key: string) {\n const rawValue = await adapter.getBuffer(key);\n return rawValue ? decode(rawValue) as T : null;\n },\n getItemTtl: async (key: string) => await adapter.ttl(key),\n hasItem: async (key: string) => await adapter.has(key),\n removeItem: async (key: string) => await adapter.delete(key) === 1,\n setItem: async (key: string, value: any) => await adapter.setBuffer(key, encode(value)) === 'OK',\n async setItemWithTtl(key: string, ttlSeconds: number, value: any) {\n return await adapter.setBufferEx(key, ttlSeconds, encode(value)) === 'OK';\n },\n async setItemWithTtlIfAbsent(key: string, ttlSeconds: number, value: any) {\n return await adapter.setBufferExNx(key, ttlSeconds, encode(value)) === 'OK';\n },\n });\n}\n"],"mappings":";;AAWA,IAAI,CAAC,6BACD,QAAQ,KAAK,qFAAqF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqDtG,SAAgB,0BAA0B,SAAuD;CAC7F,OAAO,OAAO,OAAO;EACjB,MAAM,iBAA8B,KAAa;GAC7C,MAAM,WAAW,MAAM,QAAQ,mBAAmB,GAAG;GACrD,OAAO,WAAW,OAAO,QAAQ,IAAS;EAC9C;EACA,MAAM,QAAqB,KAAa;GACpC,MAAM,WAAW,MAAM,QAAQ,UAAU,GAAG;GAC5C,OAAO,WAAW,OAAO,QAAQ,IAAS;EAC9C;EACA,YAAY,OAAO,QAAgB,MAAM,QAAQ,IAAI,GAAG;EACxD,SAAS,OAAO,QAAgB,MAAM,QAAQ,IAAI,GAAG;EACrD,YAAY,OAAO,QAAgB,MAAM,QAAQ,OAAO,GAAG,MAAM;EACjE,SAAS,OAAO,KAAa,UAAe,MAAM,QAAQ,UAAU,KAAK,OAAO,KAAK,CAAC,MAAM;EAC5F,MAAM,eAAe,KAAa,YAAoB,OAAY;GAC9D,OAAO,MAAM,QAAQ,YAAY,KAAK,YAAY,OAAO,KAAK,CAAC,MAAM;EACzE;EACA,MAAM,uBAAuB,KAAa,YAAoB,OAAY;GACtE,OAAO,MAAM,QAAQ,cAAc,KAAK,YAAY,OAAO,KAAK,CAAC,MAAM;EAC3E;CACJ,CAAC;AACL"}
|
package/dist/time.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time.d.ts","names":[],"sources":["../src/time.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"time.d.ts","names":[],"sources":["../src/time.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;wBAoBsB,MAAM,YAAY,SAAS,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;wBA2CzC,aAAa,YAAY,SAAS,cAAc"}
|
package/dist/time.js
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
* await delay(5000, controller.signal);
|
|
20
20
|
* ```
|
|
21
21
|
*/
|
|
22
|
-
function delay(ms, signal) {
|
|
23
|
-
return new Promise((resolve) => {
|
|
22
|
+
async function delay(ms, signal) {
|
|
23
|
+
return await new Promise((resolve) => {
|
|
24
24
|
if (signal?.aborted) {
|
|
25
25
|
resolve();
|
|
26
26
|
return;
|
|
@@ -58,8 +58,8 @@ function delay(ms, signal) {
|
|
|
58
58
|
* }
|
|
59
59
|
* ```
|
|
60
60
|
*/
|
|
61
|
-
function delayOrThrow(ms, signal) {
|
|
62
|
-
return new Promise((resolve, reject) => {
|
|
61
|
+
async function delayOrThrow(ms, signal) {
|
|
62
|
+
return await new Promise((resolve, reject) => {
|
|
63
63
|
if (signal?.aborted) {
|
|
64
64
|
reject(signal.reason);
|
|
65
65
|
return;
|
package/dist/time.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time.js","names":[],"sources":["../src/time.ts"],"sourcesContent":["/**\n * Waits until a duration elapses or cancellation occurs without rejecting on cancellation.\n *\n * @remarks\n * The promise resolves without a value when the timer completes or the signal aborts.\n * An already aborted signal resolves immediately; completion removes the abort listener and clears the timer.\n *\n * @param ms - The delay duration in milliseconds, using native `setTimeout` timing rules.\n * @param signal - The signal that ends the wait early.\n *\n * @example\n *\n * ```ts\n * import { delay } from '@kikiutils/shared/time';\n *\n * const controller = new AbortController();\n * controller.abort();\n * await delay(5000, controller.signal);\n * ```\n */\nexport function delay(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n\n const done = () => {\n // eslint-disable-next-line ts/no-use-before-define\n clearTimeout(timeout);\n signal?.removeEventListener('abort', done);\n resolve();\n };\n\n const timeout = setTimeout(done, ms);\n signal?.addEventListener('abort', done, { once: true });\n });\n}\n\n/**\n * Waits until a duration elapses and rejects on cancellation.\n *\n * @remarks\n * The promise resolves without a value when the timer completes and rejects with `signal.reason` if aborted.\n * An already aborted signal rejects immediately. Aborting clears the timer; timer completion removes the listener.\n *\n * @param ms - The delay duration in milliseconds, using native `setTimeout` timing rules.\n * @param signal - The signal that cancels the wait.\n *\n * @example\n *\n * ```ts\n * import { delayOrThrow } from '@kikiutils/shared/time';\n *\n * const controller = new AbortController();\n * controller.abort(new Error('Canceled'));\n * try {\n * await delayOrThrow(5000, controller.signal);\n * } catch (error) {\n * console.error(error);\n * }\n * ```\n */\nexport function delayOrThrow(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n\n const onAbort = () => {\n // eslint-disable-next-line ts/no-use-before-define\n clearTimeout(timeout);\n reject(signal!.reason);\n };\n\n const timeout = setTimeout(\n () => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n },\n ms,\n );\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoBA,
|
|
1
|
+
{"version":3,"file":"time.js","names":[],"sources":["../src/time.ts"],"sourcesContent":["/**\n * Waits until a duration elapses or cancellation occurs without rejecting on cancellation.\n *\n * @remarks\n * The promise resolves without a value when the timer completes or the signal aborts.\n * An already aborted signal resolves immediately; completion removes the abort listener and clears the timer.\n *\n * @param ms - The delay duration in milliseconds, using native `setTimeout` timing rules.\n * @param signal - The signal that ends the wait early.\n *\n * @example\n *\n * ```ts\n * import { delay } from '@kikiutils/shared/time';\n *\n * const controller = new AbortController();\n * controller.abort();\n * await delay(5000, controller.signal);\n * ```\n */\nexport async function delay(ms: number, signal?: AbortSignal): Promise<void> {\n return await new Promise((resolve) => {\n if (signal?.aborted) {\n resolve();\n return;\n }\n\n const done = () => {\n // eslint-disable-next-line ts/no-use-before-define\n clearTimeout(timeout);\n signal?.removeEventListener('abort', done);\n resolve();\n };\n\n const timeout = setTimeout(done, ms);\n signal?.addEventListener('abort', done, { once: true });\n });\n}\n\n/**\n * Waits until a duration elapses and rejects on cancellation.\n *\n * @remarks\n * The promise resolves without a value when the timer completes and rejects with `signal.reason` if aborted.\n * An already aborted signal rejects immediately. Aborting clears the timer; timer completion removes the listener.\n *\n * @param ms - The delay duration in milliseconds, using native `setTimeout` timing rules.\n * @param signal - The signal that cancels the wait.\n *\n * @example\n *\n * ```ts\n * import { delayOrThrow } from '@kikiutils/shared/time';\n *\n * const controller = new AbortController();\n * controller.abort(new Error('Canceled'));\n * try {\n * await delayOrThrow(5000, controller.signal);\n * } catch (error) {\n * console.error(error);\n * }\n * ```\n */\nexport async function delayOrThrow(ms: number, signal?: AbortSignal): Promise<void> {\n return await new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason);\n return;\n }\n\n const onAbort = () => {\n // eslint-disable-next-line ts/no-use-before-define\n clearTimeout(timeout);\n reject(signal!.reason);\n };\n\n const timeout = setTimeout(\n () => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n },\n ms,\n );\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoBA,eAAsB,MAAM,IAAY,QAAqC;CACzE,OAAO,MAAM,IAAI,SAAS,YAAY;EAClC,IAAI,QAAQ,SAAS;GACjB,QAAQ;GACR;EACJ;EAEA,MAAM,aAAa;GAEf,aAAa,OAAO;GACpB,QAAQ,oBAAoB,SAAS,IAAI;GACzC,QAAQ;EACZ;EAEA,MAAM,UAAU,WAAW,MAAM,EAAE;EACnC,QAAQ,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;CAC1D,CAAC;AACL;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,eAAsB,aAAa,IAAY,QAAqC;CAChF,OAAO,MAAM,IAAI,SAAS,SAAS,WAAW;EAC1C,IAAI,QAAQ,SAAS;GACjB,OAAO,OAAO,MAAM;GACpB;EACJ;EAEA,MAAM,gBAAgB;GAElB,aAAa,OAAO;GACpB,OAAO,OAAQ,MAAM;EACzB;EAEA,MAAM,UAAU,iBACN;GACF,QAAQ,oBAAoB,SAAS,OAAO;GAC5C,QAAQ;EACZ,GACA,EACJ;EAEA,QAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;CAC7D,CAAC;AACL"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kikiutils/shared",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "20.1.
|
|
4
|
+
"version": "20.1.2",
|
|
5
5
|
"description": "A lightweight and modular utility library for modern JavaScript and TypeScript — includes secure hashing, flexible logging, datetime tools, Vue/web helpers, storage abstraction, and more.",
|
|
6
6
|
"author": "kiki-kanri",
|
|
7
7
|
"license": "MIT",
|
|
@@ -32,9 +32,6 @@
|
|
|
32
32
|
"sideEffects": false,
|
|
33
33
|
"exports": {
|
|
34
34
|
"./buffer": "./dist/buffer.js",
|
|
35
|
-
"./classes/enhanced-date": "./dist/classes/enhanced-date.js",
|
|
36
|
-
"./classes/path": "./dist/classes/path.js",
|
|
37
|
-
"./classes/precision-number": "./dist/classes/precision-number.js",
|
|
38
35
|
"./clipboard": "./dist/clipboard.js",
|
|
39
36
|
"./crypto-hash": "./dist/crypto-hash.js",
|
|
40
37
|
"./datetime": "./dist/datetime.js",
|
|
@@ -47,24 +44,43 @@
|
|
|
47
44
|
"./hash": "./dist/hash.js",
|
|
48
45
|
"./logger": "./dist/logger/index.js",
|
|
49
46
|
"./logger/node": "./dist/logger/node/index.js",
|
|
50
|
-
"./logger/node/types":
|
|
51
|
-
|
|
47
|
+
"./logger/node/types": {
|
|
48
|
+
"types": "./dist/logger/node/types.d.ts",
|
|
49
|
+
"default": null
|
|
50
|
+
},
|
|
51
|
+
"./logger/types": {
|
|
52
|
+
"types": "./dist/logger/types.d.ts",
|
|
53
|
+
"default": null
|
|
54
|
+
},
|
|
52
55
|
"./math": "./dist/math.js",
|
|
53
56
|
"./number": "./dist/number.js",
|
|
54
57
|
"./random": "./dist/random.js",
|
|
55
|
-
"./redis/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
"./storages/redis/types": "./dist/storages/redis/types.js",
|
|
58
|
+
"./storages/redis/types": {
|
|
59
|
+
"types": "./dist/storages/redis/types.d.ts",
|
|
60
|
+
"default": null
|
|
61
|
+
},
|
|
60
62
|
"./string": "./dist/string.js",
|
|
61
63
|
"./time": "./dist/time.js",
|
|
62
|
-
"./types":
|
|
63
|
-
|
|
64
|
-
|
|
64
|
+
"./types": {
|
|
65
|
+
"types": "./dist/types/index.d.ts",
|
|
66
|
+
"default": null
|
|
67
|
+
},
|
|
65
68
|
"./url": "./dist/url.js",
|
|
66
69
|
"./vue": "./dist/vue.js",
|
|
67
70
|
"./web": "./dist/web.js",
|
|
71
|
+
"./classes/*": "./dist/classes/*.js",
|
|
72
|
+
"./logger/*": "./dist/logger/*.js",
|
|
73
|
+
"./redis/*": "./dist/redis/*.js",
|
|
74
|
+
"./storages/*": "./dist/storages/*.js",
|
|
75
|
+
"./types/*": {
|
|
76
|
+
"types": "./dist/types/*.d.ts",
|
|
77
|
+
"default": null
|
|
78
|
+
},
|
|
79
|
+
"./logger/_internals/*": null,
|
|
80
|
+
"./logger/index": null,
|
|
81
|
+
"./logger/node/_internals/*": null,
|
|
82
|
+
"./logger/node/index": null,
|
|
83
|
+
"./types/index": null,
|
|
68
84
|
"./package.json": "./package.json"
|
|
69
85
|
},
|
|
70
86
|
"files": [
|
|
@@ -143,35 +159,39 @@
|
|
|
143
159
|
"optional": true
|
|
144
160
|
}
|
|
145
161
|
},
|
|
162
|
+
"dependencies": {
|
|
163
|
+
"ts-extras": "^2.0.0",
|
|
164
|
+
"type-fest": "^5.10.0"
|
|
165
|
+
},
|
|
146
166
|
"devDependencies": {
|
|
147
167
|
"@antfu/eslint-config": "^10.0.0",
|
|
148
168
|
"@arethetypeswrong/core": "^0.18.5",
|
|
149
|
-
"@kikiutils/eslint-config": "^
|
|
169
|
+
"@kikiutils/eslint-config": "^11.0.1",
|
|
150
170
|
"@kikiutils/tsconfigs": "^7.0.0",
|
|
151
171
|
"@noble/hashes": "^2.4.0",
|
|
152
172
|
"@types/node": "^26.6.6",
|
|
153
|
-
"@vitest/coverage-v8": "^5.0
|
|
173
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
154
174
|
"async-validator": "^4.2.5",
|
|
155
|
-
"bson": "^7.3.
|
|
175
|
+
"bson": "^7.3.4",
|
|
156
176
|
"changelogen": "^0.6.2",
|
|
157
177
|
"cross-env": "^10.1.0",
|
|
158
178
|
"date-fns": "^4.4.0",
|
|
159
179
|
"decimal.js": "^10.6.0",
|
|
160
|
-
"element-plus": "^2.14.
|
|
180
|
+
"element-plus": "^2.14.8",
|
|
161
181
|
"eslint": "^10.14.0",
|
|
162
182
|
"file-type": "^22.3.0",
|
|
163
|
-
"jsdom": "^
|
|
164
|
-
"knip": "^6.
|
|
183
|
+
"jsdom": "^31.0.0",
|
|
184
|
+
"knip": "^6.45.1",
|
|
165
185
|
"lru-cache": "^11.5.3",
|
|
166
186
|
"millify": "^6.1.0",
|
|
167
|
-
"msgpackr": "^2.1.
|
|
187
|
+
"msgpackr": "^2.1.4",
|
|
168
188
|
"publint": "^0.3.25",
|
|
169
|
-
"rotating-file-stream": "^
|
|
170
|
-
"tsdown": "^0.23.
|
|
189
|
+
"rotating-file-stream": "^4.0.0",
|
|
190
|
+
"tsdown": "^0.23.7",
|
|
171
191
|
"typescript": "^6.0.3",
|
|
172
192
|
"unplugin-unused": "^0.6.0",
|
|
173
|
-
"vitest": "^5.0
|
|
174
|
-
"vue": "^3.5.
|
|
175
|
-
"vue-router": "^5.
|
|
193
|
+
"vitest": "^5.1.0",
|
|
194
|
+
"vue": "^3.5.45",
|
|
195
|
+
"vue-router": "^5.5.0"
|
|
176
196
|
}
|
|
177
197
|
}
|