superjs-core 0.3.3 → 0.3.5
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/async/index.d.ts +84 -1
- package/dist/async/index.js +151 -0
- package/dist/async/index.js.map +1 -1
- package/dist/collection/index.d.ts +7 -1
- package/dist/collection/index.js +55 -0
- package/dist/collection/index.js.map +1 -1
- package/dist/date/index.d.ts +71 -1
- package/dist/date/index.js +121 -1
- package/dist/date/index.js.map +1 -1
- package/dist/error/index.d.ts +148 -0
- package/dist/error/index.js +115 -0
- package/dist/error/index.js.map +1 -0
- package/dist/index-BgG21uJC.d.ts +166 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +506 -0
- package/dist/index.js.map +1 -1
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.js +214 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/transports.d.ts +1 -0
- package/dist/logger/transports.js +122 -0
- package/dist/logger/transports.js.map +1 -0
- package/dist/math/index.d.ts +59 -1
- package/dist/math/index.js +69 -0
- package/dist/math/index.js.map +1 -1
- package/dist/string/index.d.ts +41 -1
- package/dist/string/index.js +117 -0
- package/dist/string/index.js.map +1 -1
- package/dist/validation/index.d.ts +106 -0
- package/dist/validation/index.js +183 -0
- package/dist/validation/index.js.map +1 -0
- package/package.json +17 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { h as LogFn, L as LogLevel, a as Logger, i as LoggerOptions, T as Transport, c as consoleTransport, b as createBufferedTransport, d as createConsoleTransport, e as createFileTransport, f as createJsonTransport, l as logger } from '../index-BgG21uJC.js';
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/logger/logger.ts
|
|
9
|
+
var LEVEL_ORDER = { debug: 0, info: 1, warn: 2, error: 3 };
|
|
10
|
+
var Logger = class _Logger {
|
|
11
|
+
_level;
|
|
12
|
+
_name;
|
|
13
|
+
_transport;
|
|
14
|
+
_extraMeta;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this._level = options?.level ?? "info";
|
|
17
|
+
this._name = options?.name;
|
|
18
|
+
this._transport = options?.transport ?? consoleTransport;
|
|
19
|
+
this._extraMeta = /* @__PURE__ */ Object.create(null);
|
|
20
|
+
}
|
|
21
|
+
_shouldLog(target) {
|
|
22
|
+
return LEVEL_ORDER[this._level] <= LEVEL_ORDER[target];
|
|
23
|
+
}
|
|
24
|
+
_log(level, message, meta) {
|
|
25
|
+
if (level !== "error" && !this._shouldLog(level)) return;
|
|
26
|
+
const merged = /* @__PURE__ */ Object.create(null);
|
|
27
|
+
for (const key of Object.keys(this._extraMeta)) {
|
|
28
|
+
merged[key] = this._extraMeta[key];
|
|
29
|
+
}
|
|
30
|
+
if (meta !== void 0) {
|
|
31
|
+
for (const key of Object.keys(meta)) {
|
|
32
|
+
merged[key] = meta[key];
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const label = this._name !== void 0 ? `[${this._name}] ${message}` : message;
|
|
36
|
+
const finalMeta = Object.keys(merged).length > 0 ? merged : void 0;
|
|
37
|
+
this._transport.log(level, label, finalMeta);
|
|
38
|
+
}
|
|
39
|
+
/** Log at `debug` level. Only emitted when the current level is `'debug'`. */
|
|
40
|
+
debug = (message, meta) => this._log("debug", message, meta);
|
|
41
|
+
/** Log at `info` level. Emitted when level is `'debug'` or `'info'`. */
|
|
42
|
+
info = (message, meta) => this._log("info", message, meta);
|
|
43
|
+
/** Log at `warn` level. Emitted when level is `'debug'`, `'info'`, or `'warn'`. */
|
|
44
|
+
warn = (message, meta) => this._log("warn", message, meta);
|
|
45
|
+
/** Log at `error` level. Always emitted regardless of current level. */
|
|
46
|
+
error = (message, meta) => this._log("error", message, meta);
|
|
47
|
+
/**
|
|
48
|
+
* Creates a child logger that inherits the parent's level, name, and transport,
|
|
49
|
+
* but merges `extraMeta` into every log call. Child metadata is shallow-merged
|
|
50
|
+
* on top of the parent's inherited metadata.
|
|
51
|
+
*
|
|
52
|
+
* @param extraMeta - Additional context to include in every log entry.
|
|
53
|
+
*/
|
|
54
|
+
child(extraMeta) {
|
|
55
|
+
const child = new _Logger({
|
|
56
|
+
level: this._level,
|
|
57
|
+
name: this._name,
|
|
58
|
+
transport: this._transport
|
|
59
|
+
});
|
|
60
|
+
const inherited = /* @__PURE__ */ Object.create(null);
|
|
61
|
+
for (const key of Object.keys(this._extraMeta)) {
|
|
62
|
+
inherited[key] = this._extraMeta[key];
|
|
63
|
+
}
|
|
64
|
+
for (const key of Object.keys(extraMeta)) {
|
|
65
|
+
inherited[key] = extraMeta[key];
|
|
66
|
+
}
|
|
67
|
+
child._extraMeta = inherited;
|
|
68
|
+
return child;
|
|
69
|
+
}
|
|
70
|
+
/** Updates the minimum log level for this instance. */
|
|
71
|
+
setLevel(level) {
|
|
72
|
+
this._level = level;
|
|
73
|
+
}
|
|
74
|
+
/** Returns the current minimum log level. */
|
|
75
|
+
getLevel() {
|
|
76
|
+
return this._level;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Creates a new named Logger.
|
|
80
|
+
* Convenience shorthand for `new Logger({ ...options, name })`.
|
|
81
|
+
*
|
|
82
|
+
* @param name - The name tag shown in log output.
|
|
83
|
+
* @param options - Additional configuration.
|
|
84
|
+
*/
|
|
85
|
+
static create(name, options) {
|
|
86
|
+
return new _Logger({ ...options, name });
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var consoleTransport = {
|
|
90
|
+
log(level, message, meta) {
|
|
91
|
+
const metaStr = meta !== void 0 && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
|
|
92
|
+
console.log(`[${level.toUpperCase()}] ${message}${metaStr}`);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var logger = new Logger();
|
|
96
|
+
|
|
97
|
+
// src/logger/transports.ts
|
|
98
|
+
var LEVEL_COLORS = {
|
|
99
|
+
debug: "\x1B[90m",
|
|
100
|
+
info: "\x1B[34m",
|
|
101
|
+
warn: "\x1B[33m",
|
|
102
|
+
error: "\x1B[31m"
|
|
103
|
+
};
|
|
104
|
+
var RESET = "\x1B[0m";
|
|
105
|
+
function createConsoleTransport(options) {
|
|
106
|
+
const useColors = options?.colors !== false;
|
|
107
|
+
const showTimestamp = options?.timestamp ?? false;
|
|
108
|
+
return {
|
|
109
|
+
log(level, message, meta) {
|
|
110
|
+
const parts = [];
|
|
111
|
+
if (showTimestamp) {
|
|
112
|
+
parts.push((/* @__PURE__ */ new Date()).toISOString());
|
|
113
|
+
}
|
|
114
|
+
if (useColors) {
|
|
115
|
+
const color = LEVEL_COLORS[level];
|
|
116
|
+
parts.push(`${color}[${level.toUpperCase()}]${RESET}`);
|
|
117
|
+
} else {
|
|
118
|
+
parts.push(`[${level.toUpperCase()}]`);
|
|
119
|
+
}
|
|
120
|
+
parts.push(message);
|
|
121
|
+
if (meta !== void 0 && Object.keys(meta).length > 0) {
|
|
122
|
+
parts.push(JSON.stringify(meta));
|
|
123
|
+
}
|
|
124
|
+
console.log(parts.join(" "));
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function createJsonTransport(options) {
|
|
129
|
+
const writeStream = options?.stream ?? (typeof process !== "undefined" && typeof process.stdout !== "undefined" && typeof process.stdout.write === "function" ? process.stdout : void 0);
|
|
130
|
+
return {
|
|
131
|
+
log(level, message, meta) {
|
|
132
|
+
const entry = {
|
|
133
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
134
|
+
level,
|
|
135
|
+
message
|
|
136
|
+
};
|
|
137
|
+
if (meta !== void 0 && Object.keys(meta).length > 0) {
|
|
138
|
+
entry.meta = meta;
|
|
139
|
+
}
|
|
140
|
+
const line = JSON.stringify(entry);
|
|
141
|
+
if (writeStream !== void 0) {
|
|
142
|
+
writeStream.write(line + "\n");
|
|
143
|
+
} else {
|
|
144
|
+
console.log(line);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function createFileTransport(filename, _options) {
|
|
150
|
+
let fs = null;
|
|
151
|
+
try {
|
|
152
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
153
|
+
const m = __require("fs");
|
|
154
|
+
fs = m;
|
|
155
|
+
}
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
log(level, message, meta) {
|
|
160
|
+
if (fs === null) return;
|
|
161
|
+
const metaStr = meta !== void 0 && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
|
|
162
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level.toUpperCase()}] ${message}${metaStr}
|
|
163
|
+
`;
|
|
164
|
+
try {
|
|
165
|
+
fs.appendFileSync(filename, line);
|
|
166
|
+
} catch {
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function createBufferedTransport(transport, options) {
|
|
172
|
+
const maxSize = options?.maxSize ?? 100;
|
|
173
|
+
const flushIntervalMs = options?.flushIntervalMs ?? 5e3;
|
|
174
|
+
const buffer = [];
|
|
175
|
+
let timer;
|
|
176
|
+
function flush() {
|
|
177
|
+
if (timer !== void 0) {
|
|
178
|
+
clearTimeout(timer);
|
|
179
|
+
timer = void 0;
|
|
180
|
+
}
|
|
181
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
182
|
+
const entry = buffer[i];
|
|
183
|
+
transport.log(entry.level, entry.message, entry.meta);
|
|
184
|
+
}
|
|
185
|
+
buffer.length = 0;
|
|
186
|
+
}
|
|
187
|
+
function scheduleFlush() {
|
|
188
|
+
if (timer !== void 0 || flushIntervalMs <= 0) return;
|
|
189
|
+
timer = setTimeout(() => {
|
|
190
|
+
timer = void 0;
|
|
191
|
+
flush();
|
|
192
|
+
}, flushIntervalMs);
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
log(level, message, meta) {
|
|
196
|
+
buffer.push({ level, message, meta });
|
|
197
|
+
if (buffer.length >= maxSize) {
|
|
198
|
+
flush();
|
|
199
|
+
} else {
|
|
200
|
+
scheduleFlush();
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
export {
|
|
206
|
+
Logger,
|
|
207
|
+
consoleTransport,
|
|
208
|
+
createBufferedTransport,
|
|
209
|
+
createConsoleTransport,
|
|
210
|
+
createFileTransport,
|
|
211
|
+
createJsonTransport,
|
|
212
|
+
logger
|
|
213
|
+
};
|
|
214
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/logger/logger.ts","../../src/logger/transports.ts"],"sourcesContent":["/**\n * Represents the severity level of a log entry.\n * Ordered from least to most severe: debug < info < warn < error.\n */\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\n/**\n * Log function signature bound to a specific severity level.\n */\nexport type LogFn = (message: string, meta?: Record<string, unknown>) => void\n\n/**\n * Configuration options for creating a Logger instance.\n */\nexport interface LoggerOptions {\n /** Minimum log level to output (default: 'info'). */\n level?: LogLevel\n /** Optional name tag prepended to every message as `[name]`. */\n name?: string\n /** Custom transport; defaults to {@link consoleTransport}. */\n transport?: Transport\n}\n\n/**\n * A transport handles the formatted output of log entries.\n * Implementations write to stdout, files, buffers, or remote services.\n */\nexport interface Transport {\n log(level: LogLevel, message: string, meta?: Record<string, unknown>): void\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 }\n\n/**\n * Structured logger with level filtering, child loggers, and pluggable transport.\n *\n * @example\n * ```ts\n * const log = new Logger({ level: 'debug', name: 'app' })\n * log.info('server started', { port: 3000 })\n *\n * const child = log.child({ requestId: 'abc-123' })\n * child.warn('slow query', { durationMs: 450 })\n * ```\n */\nexport class Logger {\n private _level: LogLevel\n private _name?: string\n private _transport: Transport\n private _extraMeta: Record<string, unknown>\n\n constructor(options?: LoggerOptions) {\n this._level = options?.level ?? 'info'\n this._name = options?.name\n this._transport = options?.transport ?? consoleTransport\n this._extraMeta = Object.create(null)\n }\n\n private _shouldLog(target: LogLevel): boolean {\n return LEVEL_ORDER[this._level] <= LEVEL_ORDER[target]\n }\n\n private _log(level: LogLevel, message: string, meta?: Record<string, unknown>): void {\n if (level !== 'error' && !this._shouldLog(level)) return\n\n const merged: Record<string, unknown> = Object.create(null)\n for (const key of Object.keys(this._extraMeta)) {\n merged[key] = (this._extraMeta as Record<string, unknown>)[key]!\n }\n if (meta !== undefined) {\n for (const key of Object.keys(meta)) {\n merged[key] = meta[key]!\n }\n }\n\n const label = this._name !== undefined ? `[${this._name}] ${message}` : message\n const finalMeta = Object.keys(merged).length > 0 ? merged : undefined\n this._transport.log(level, label, finalMeta)\n }\n\n /** Log at `debug` level. Only emitted when the current level is `'debug'`. */\n debug: LogFn = (message, meta?) => this._log('debug', message, meta)\n\n /** Log at `info` level. Emitted when level is `'debug'` or `'info'`. */\n info: LogFn = (message, meta?) => this._log('info', message, meta)\n\n /** Log at `warn` level. Emitted when level is `'debug'`, `'info'`, or `'warn'`. */\n warn: LogFn = (message, meta?) => this._log('warn', message, meta)\n\n /** Log at `error` level. Always emitted regardless of current level. */\n error: LogFn = (message, meta?) => this._log('error', message, meta)\n\n /**\n * Creates a child logger that inherits the parent's level, name, and transport,\n * but merges `extraMeta` into every log call. Child metadata is shallow-merged\n * on top of the parent's inherited metadata.\n *\n * @param extraMeta - Additional context to include in every log entry.\n */\n child(extraMeta: Record<string, unknown>): Logger {\n const child = new Logger({\n level: this._level,\n name: this._name,\n transport: this._transport,\n })\n const inherited: Record<string, unknown> = Object.create(null)\n for (const key of Object.keys(this._extraMeta)) {\n inherited[key] = (this._extraMeta as Record<string, unknown>)[key]!\n }\n for (const key of Object.keys(extraMeta)) {\n inherited[key] = extraMeta[key]!\n }\n child._extraMeta = inherited\n return child\n }\n\n /** Updates the minimum log level for this instance. */\n setLevel(level: LogLevel): void {\n this._level = level\n }\n\n /** Returns the current minimum log level. */\n getLevel(): LogLevel {\n return this._level\n }\n\n /**\n * Creates a new named Logger.\n * Convenience shorthand for `new Logger({ ...options, name })`.\n *\n * @param name - The name tag shown in log output.\n * @param options - Additional configuration.\n */\n static create(name: string, options?: LoggerOptions): Logger {\n return new Logger({ ...options, name })\n }\n}\n\n/**\n * Default transport that writes formatted log lines to `console.log`.\n *\n * Output format: `[LEVEL] message {meta}`\n *\n * When a logger has a name, the format becomes: `[LEVEL] [name] message {meta}`\n */\nexport const consoleTransport: Transport = {\n log(level, message, meta) {\n const metaStr =\n meta !== undefined && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : ''\n console.log(`[${level.toUpperCase()}] ${message}${metaStr}`)\n },\n}\n\n/**\n * Default logger instance at `'info'` level with no name.\n */\nexport const logger: Logger = new Logger()\n","import type { LogLevel } from './logger.js'\n\n/**\n * A transport handles the formatted output of log entries.\n */\nexport interface Transport {\n log(level: LogLevel, message: string, meta?: Record<string, unknown>): void\n}\n\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n debug: '\\x1b[90m',\n info: '\\x1b[34m',\n warn: '\\x1b[33m',\n error: '\\x1b[31m',\n}\n\nconst RESET = '\\x1b[0m'\n\n/**\n * Creates a console transport that writes formatted log lines to `console.log`.\n *\n * The level prefix is optionally colored using ANSI escape codes:\n * - `debug` → gray\n * - `info` → blue\n * - `warn` → yellow\n * - `error` → red\n *\n * @param options - Configuration for the console transport.\n * @param options.colors - Enable ANSI color output (default: `true`).\n * @param options.timestamp - Prepend an ISO-8601 timestamp (default: `false`).\n */\nexport function createConsoleTransport(options?: {\n colors?: boolean\n timestamp?: boolean\n}): Transport {\n const useColors = options?.colors !== false\n const showTimestamp = options?.timestamp ?? false\n\n return {\n log(level, message, meta) {\n const parts: string[] = []\n\n if (showTimestamp) {\n parts.push(new Date().toISOString())\n }\n\n if (useColors) {\n const color = LEVEL_COLORS[level]\n parts.push(`${color}[${level.toUpperCase()}]${RESET}`)\n } else {\n parts.push(`[${level.toUpperCase()}]`)\n }\n\n parts.push(message)\n\n if (meta !== undefined && Object.keys(meta).length > 0) {\n parts.push(JSON.stringify(meta))\n }\n\n console.log(parts.join(' '))\n },\n }\n}\n\n/**\n * Creates a transport that outputs structured JSON lines.\n *\n * Each log entry is serialized as a single JSON object with\n * `timestamp`, `level`, `message`, and optional `meta` fields.\n *\n * @param options - Configuration for the JSON transport.\n * @param options.stream - A writable stream (e.g. `process.stdout`).\n * Defaults to `process.stdout` in Node.js, falls\n * back to `console.log` in browsers.\n */\nexport function createJsonTransport(options?: {\n stream?: { write(data: string): void }\n}): Transport {\n const writeStream =\n options?.stream ??\n (typeof process !== 'undefined' &&\n typeof process.stdout !== 'undefined' &&\n typeof process.stdout.write === 'function'\n ? (process.stdout as { write(data: string): void })\n : undefined)\n\n return {\n log(level, message, meta) {\n const entry: Record<string, unknown> = {\n timestamp: new Date().toISOString(),\n level,\n message,\n }\n if (meta !== undefined && Object.keys(meta).length > 0) {\n entry.meta = meta\n }\n const line = JSON.stringify(entry)\n if (writeStream !== undefined) {\n writeStream.write(line + '\\n')\n } else {\n console.log(line)\n }\n },\n }\n}\n\n/**\n * Creates a transport that appends log entries to a file.\n *\n * Each line is formatted as: `[timestamp] [LEVEL] message {meta}`\n *\n * ⚠️ Node.js only. Silently discards log entries when `fs` is unavailable\n * (browsers, Deno, Bun — though Bun supports `fs`).\n *\n * @param filename - Path to the log file.\n * @param options - Configuration for the file transport.\n * @param options.maxSize - Maximum file size in bytes before rotation\n * (default: 10 MB). **Note:** rotation is not\n * yet implemented; this is reserved for future use.\n */\nexport function createFileTransport(\n filename: string,\n _options?: { maxSize?: number },\n): Transport {\n // Try to resolve fs synchronously at construction time\n // Uses process.versions.node as a heuristic for Node.js environment\n let fs: { appendFileSync: (path: string, data: string) => void } | null = null\n try {\n if (typeof process !== 'undefined' && process.versions?.node) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const m = require('fs') as { appendFileSync: (path: string, data: string) => void }\n fs = m\n }\n } catch {\n // fs unavailable (browser, edge runtime)\n }\n\n return {\n log(level, message, meta) {\n if (fs === null) return\n\n const metaStr =\n meta !== undefined && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : ''\n const line = `[${new Date().toISOString()}] [${level.toUpperCase()}] ${message}${metaStr}\\n`\n\n try {\n fs.appendFileSync(filename, line)\n } catch {\n // Silently drop if write fails (disk full, permissions, etc.)\n }\n },\n }\n}\n\n/**\n * Creates a buffered transport that batches log entries and flushes them\n * to the underlying transport when either the buffer size or flush interval\n * is reached (whichever comes first).\n *\n * Useful for reducing I/O pressure in high-throughput scenarios.\n *\n * @param transport - The underlying transport to flush to.\n * @param options - Configuration for the buffer.\n * @param options.maxSize - Maximum number of entries before forced flush\n * (default: 100).\n * @param options.flushIntervalMs - How often to auto-flush in milliseconds\n * (default: 5000). Set to `0` to disable\n * interval flushing.\n */\nexport function createBufferedTransport(\n transport: Transport,\n options?: { maxSize?: number; flushIntervalMs?: number },\n): Transport {\n const maxSize = options?.maxSize ?? 100\n const flushIntervalMs = options?.flushIntervalMs ?? 5000\n\n const buffer: Array<{\n level: LogLevel\n message: string\n meta?: Record<string, unknown>\n }> = []\n\n let timer: ReturnType<typeof setTimeout> | undefined\n\n function flush(): void {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n for (let i = 0; i < buffer.length; i++) {\n const entry = buffer[i]!\n transport.log(entry.level, entry.message, entry.meta)\n }\n buffer.length = 0\n }\n\n function scheduleFlush(): void {\n if (timer !== undefined || flushIntervalMs <= 0) return\n timer = setTimeout((): void => {\n timer = undefined\n flush()\n }, flushIntervalMs)\n }\n\n return {\n log(level, message, meta) {\n buffer.push({ level, message, meta })\n if (buffer.length >= maxSize) {\n flush()\n } else {\n scheduleFlush()\n }\n },\n }\n}\n"],"mappings":";;;;;;;;AA+BA,IAAM,cAAwC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE;AAc9E,IAAM,SAAN,MAAM,QAAO;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,SAAyB;AACnC,SAAK,SAAS,SAAS,SAAS;AAChC,SAAK,QAAQ,SAAS;AACtB,SAAK,aAAa,SAAS,aAAa;AACxC,SAAK,aAAa,uBAAO,OAAO,IAAI;AAAA,EACtC;AAAA,EAEQ,WAAW,QAA2B;AAC5C,WAAO,YAAY,KAAK,MAAM,KAAK,YAAY,MAAM;AAAA,EACvD;AAAA,EAEQ,KAAK,OAAiB,SAAiB,MAAsC;AACnF,QAAI,UAAU,WAAW,CAAC,KAAK,WAAW,KAAK,EAAG;AAElD,UAAM,SAAkC,uBAAO,OAAO,IAAI;AAC1D,eAAW,OAAO,OAAO,KAAK,KAAK,UAAU,GAAG;AAC9C,aAAO,GAAG,IAAK,KAAK,WAAuC,GAAG;AAAA,IAChE;AACA,QAAI,SAAS,QAAW;AACtB,iBAAW,OAAO,OAAO,KAAK,IAAI,GAAG;AACnC,eAAO,GAAG,IAAI,KAAK,GAAG;AAAA,MACxB;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,UAAU,SAAY,IAAI,KAAK,KAAK,KAAK,OAAO,KAAK;AACxE,UAAM,YAAY,OAAO,KAAK,MAAM,EAAE,SAAS,IAAI,SAAS;AAC5D,SAAK,WAAW,IAAI,OAAO,OAAO,SAAS;AAAA,EAC7C;AAAA;AAAA,EAGA,QAAe,CAAC,SAAS,SAAU,KAAK,KAAK,SAAS,SAAS,IAAI;AAAA;AAAA,EAGnE,OAAc,CAAC,SAAS,SAAU,KAAK,KAAK,QAAQ,SAAS,IAAI;AAAA;AAAA,EAGjE,OAAc,CAAC,SAAS,SAAU,KAAK,KAAK,QAAQ,SAAS,IAAI;AAAA;AAAA,EAGjE,QAAe,CAAC,SAAS,SAAU,KAAK,KAAK,SAAS,SAAS,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASnE,MAAM,WAA4C;AAChD,UAAM,QAAQ,IAAI,QAAO;AAAA,MACvB,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,IAClB,CAAC;AACD,UAAM,YAAqC,uBAAO,OAAO,IAAI;AAC7D,eAAW,OAAO,OAAO,KAAK,KAAK,UAAU,GAAG;AAC9C,gBAAU,GAAG,IAAK,KAAK,WAAuC,GAAG;AAAA,IACnE;AACA,eAAW,OAAO,OAAO,KAAK,SAAS,GAAG;AACxC,gBAAU,GAAG,IAAI,UAAU,GAAG;AAAA,IAChC;AACA,UAAM,aAAa;AACnB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,SAAS,OAAuB;AAC9B,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,WAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,OAAO,MAAc,SAAiC;AAC3D,WAAO,IAAI,QAAO,EAAE,GAAG,SAAS,KAAK,CAAC;AAAA,EACxC;AACF;AASO,IAAM,mBAA8B;AAAA,EACzC,IAAI,OAAO,SAAS,MAAM;AACxB,UAAM,UACJ,SAAS,UAAa,OAAO,KAAK,IAAI,EAAE,SAAS,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AACpF,YAAQ,IAAI,IAAI,MAAM,YAAY,CAAC,KAAK,OAAO,GAAG,OAAO,EAAE;AAAA,EAC7D;AACF;AAKO,IAAM,SAAiB,IAAI,OAAO;;;ACnJzC,IAAM,eAAyC;AAAA,EAC7C,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,OAAO;AACT;AAEA,IAAM,QAAQ;AAeP,SAAS,uBAAuB,SAGzB;AACZ,QAAM,YAAY,SAAS,WAAW;AACtC,QAAM,gBAAgB,SAAS,aAAa;AAE5C,SAAO;AAAA,IACL,IAAI,OAAO,SAAS,MAAM;AACxB,YAAM,QAAkB,CAAC;AAEzB,UAAI,eAAe;AACjB,cAAM,MAAK,oBAAI,KAAK,GAAE,YAAY,CAAC;AAAA,MACrC;AAEA,UAAI,WAAW;AACb,cAAM,QAAQ,aAAa,KAAK;AAChC,cAAM,KAAK,GAAG,KAAK,IAAI,MAAM,YAAY,CAAC,IAAI,KAAK,EAAE;AAAA,MACvD,OAAO;AACL,cAAM,KAAK,IAAI,MAAM,YAAY,CAAC,GAAG;AAAA,MACvC;AAEA,YAAM,KAAK,OAAO;AAElB,UAAI,SAAS,UAAa,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACtD,cAAM,KAAK,KAAK,UAAU,IAAI,CAAC;AAAA,MACjC;AAEA,cAAQ,IAAI,MAAM,KAAK,GAAG,CAAC;AAAA,IAC7B;AAAA,EACF;AACF;AAaO,SAAS,oBAAoB,SAEtB;AACZ,QAAM,cACJ,SAAS,WACR,OAAO,YAAY,eACpB,OAAO,QAAQ,WAAW,eAC1B,OAAO,QAAQ,OAAO,UAAU,aAC3B,QAAQ,SACT;AAEN,SAAO;AAAA,IACL,IAAI,OAAO,SAAS,MAAM;AACxB,YAAM,QAAiC;AAAA,QACrC,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,QAClC;AAAA,QACA;AAAA,MACF;AACA,UAAI,SAAS,UAAa,OAAO,KAAK,IAAI,EAAE,SAAS,GAAG;AACtD,cAAM,OAAO;AAAA,MACf;AACA,YAAM,OAAO,KAAK,UAAU,KAAK;AACjC,UAAI,gBAAgB,QAAW;AAC7B,oBAAY,MAAM,OAAO,IAAI;AAAA,MAC/B,OAAO;AACL,gBAAQ,IAAI,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AACF;AAgBO,SAAS,oBACd,UACA,UACW;AAGX,MAAI,KAAsE;AAC1E,MAAI;AACF,QAAI,OAAO,YAAY,eAAe,QAAQ,UAAU,MAAM;AAE5D,YAAM,IAAI,UAAQ,IAAI;AACtB,WAAK;AAAA,IACP;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AAAA,IACL,IAAI,OAAO,SAAS,MAAM;AACxB,UAAI,OAAO,KAAM;AAEjB,YAAM,UACJ,SAAS,UAAa,OAAO,KAAK,IAAI,EAAE,SAAS,IAAI,IAAI,KAAK,UAAU,IAAI,CAAC,KAAK;AACpF,YAAM,OAAO,KAAI,oBAAI,KAAK,GAAE,YAAY,CAAC,MAAM,MAAM,YAAY,CAAC,KAAK,OAAO,GAAG,OAAO;AAAA;AAExF,UAAI;AACF,WAAG,eAAe,UAAU,IAAI;AAAA,MAClC,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAiBO,SAAS,wBACd,WACA,SACW;AACX,QAAM,UAAU,SAAS,WAAW;AACpC,QAAM,kBAAkB,SAAS,mBAAmB;AAEpD,QAAM,SAID,CAAC;AAEN,MAAI;AAEJ,WAAS,QAAc;AACrB,QAAI,UAAU,QAAW;AACvB,mBAAa,KAAK;AAClB,cAAQ;AAAA,IACV;AACA,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,QAAQ,OAAO,CAAC;AACtB,gBAAU,IAAI,MAAM,OAAO,MAAM,SAAS,MAAM,IAAI;AAAA,IACtD;AACA,WAAO,SAAS;AAAA,EAClB;AAEA,WAAS,gBAAsB;AAC7B,QAAI,UAAU,UAAa,mBAAmB,EAAG;AACjD,YAAQ,WAAW,MAAY;AAC7B,cAAQ;AACR,YAAM;AAAA,IACR,GAAG,eAAe;AAAA,EACpB;AAEA,SAAO;AAAA,IACL,IAAI,OAAO,SAAS,MAAM;AACxB,aAAO,KAAK,EAAE,OAAO,SAAS,KAAK,CAAC;AACpC,UAAI,OAAO,UAAU,SAAS;AAC5B,cAAM;AAAA,MACR,OAAO;AACL,sBAAc;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { g as Transport, b as createBufferedTransport, d as createConsoleTransport, e as createFileTransport, f as createJsonTransport } from '../index-BgG21uJC.js';
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// src/logger/transports.ts
|
|
9
|
+
var LEVEL_COLORS = {
|
|
10
|
+
debug: "\x1B[90m",
|
|
11
|
+
info: "\x1B[34m",
|
|
12
|
+
warn: "\x1B[33m",
|
|
13
|
+
error: "\x1B[31m"
|
|
14
|
+
};
|
|
15
|
+
var RESET = "\x1B[0m";
|
|
16
|
+
function createConsoleTransport(options) {
|
|
17
|
+
const useColors = options?.colors !== false;
|
|
18
|
+
const showTimestamp = options?.timestamp ?? false;
|
|
19
|
+
return {
|
|
20
|
+
log(level, message, meta) {
|
|
21
|
+
const parts = [];
|
|
22
|
+
if (showTimestamp) {
|
|
23
|
+
parts.push((/* @__PURE__ */ new Date()).toISOString());
|
|
24
|
+
}
|
|
25
|
+
if (useColors) {
|
|
26
|
+
const color = LEVEL_COLORS[level];
|
|
27
|
+
parts.push(`${color}[${level.toUpperCase()}]${RESET}`);
|
|
28
|
+
} else {
|
|
29
|
+
parts.push(`[${level.toUpperCase()}]`);
|
|
30
|
+
}
|
|
31
|
+
parts.push(message);
|
|
32
|
+
if (meta !== void 0 && Object.keys(meta).length > 0) {
|
|
33
|
+
parts.push(JSON.stringify(meta));
|
|
34
|
+
}
|
|
35
|
+
console.log(parts.join(" "));
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function createJsonTransport(options) {
|
|
40
|
+
const writeStream = options?.stream ?? (typeof process !== "undefined" && typeof process.stdout !== "undefined" && typeof process.stdout.write === "function" ? process.stdout : void 0);
|
|
41
|
+
return {
|
|
42
|
+
log(level, message, meta) {
|
|
43
|
+
const entry = {
|
|
44
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
45
|
+
level,
|
|
46
|
+
message
|
|
47
|
+
};
|
|
48
|
+
if (meta !== void 0 && Object.keys(meta).length > 0) {
|
|
49
|
+
entry.meta = meta;
|
|
50
|
+
}
|
|
51
|
+
const line = JSON.stringify(entry);
|
|
52
|
+
if (writeStream !== void 0) {
|
|
53
|
+
writeStream.write(line + "\n");
|
|
54
|
+
} else {
|
|
55
|
+
console.log(line);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function createFileTransport(filename, _options) {
|
|
61
|
+
let fs = null;
|
|
62
|
+
try {
|
|
63
|
+
if (typeof process !== "undefined" && process.versions?.node) {
|
|
64
|
+
const m = __require("fs");
|
|
65
|
+
fs = m;
|
|
66
|
+
}
|
|
67
|
+
} catch {
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
log(level, message, meta) {
|
|
71
|
+
if (fs === null) return;
|
|
72
|
+
const metaStr = meta !== void 0 && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : "";
|
|
73
|
+
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level.toUpperCase()}] ${message}${metaStr}
|
|
74
|
+
`;
|
|
75
|
+
try {
|
|
76
|
+
fs.appendFileSync(filename, line);
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function createBufferedTransport(transport, options) {
|
|
83
|
+
const maxSize = options?.maxSize ?? 100;
|
|
84
|
+
const flushIntervalMs = options?.flushIntervalMs ?? 5e3;
|
|
85
|
+
const buffer = [];
|
|
86
|
+
let timer;
|
|
87
|
+
function flush() {
|
|
88
|
+
if (timer !== void 0) {
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
timer = void 0;
|
|
91
|
+
}
|
|
92
|
+
for (let i = 0; i < buffer.length; i++) {
|
|
93
|
+
const entry = buffer[i];
|
|
94
|
+
transport.log(entry.level, entry.message, entry.meta);
|
|
95
|
+
}
|
|
96
|
+
buffer.length = 0;
|
|
97
|
+
}
|
|
98
|
+
function scheduleFlush() {
|
|
99
|
+
if (timer !== void 0 || flushIntervalMs <= 0) return;
|
|
100
|
+
timer = setTimeout(() => {
|
|
101
|
+
timer = void 0;
|
|
102
|
+
flush();
|
|
103
|
+
}, flushIntervalMs);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
log(level, message, meta) {
|
|
107
|
+
buffer.push({ level, message, meta });
|
|
108
|
+
if (buffer.length >= maxSize) {
|
|
109
|
+
flush();
|
|
110
|
+
} else {
|
|
111
|
+
scheduleFlush();
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
createBufferedTransport,
|
|
118
|
+
createConsoleTransport,
|
|
119
|
+
createFileTransport,
|
|
120
|
+
createJsonTransport
|
|
121
|
+
};
|
|
122
|
+
//# sourceMappingURL=transports.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/logger/transports.ts"],"sourcesContent":["import type { LogLevel } from './logger.js'\n\n/**\n * A transport handles the formatted output of log entries.\n */\nexport interface Transport {\n log(level: LogLevel, message: string, meta?: Record<string, unknown>): void\n}\n\nconst LEVEL_COLORS: Record<LogLevel, string> = {\n debug: '\\x1b[90m',\n info: '\\x1b[34m',\n warn: '\\x1b[33m',\n error: '\\x1b[31m',\n}\n\nconst RESET = '\\x1b[0m'\n\n/**\n * Creates a console transport that writes formatted log lines to `console.log`.\n *\n * The level prefix is optionally colored using ANSI escape codes:\n * - `debug` → gray\n * - `info` → blue\n * - `warn` → yellow\n * - `error` → red\n *\n * @param options - Configuration for the console transport.\n * @param options.colors - Enable ANSI color output (default: `true`).\n * @param options.timestamp - Prepend an ISO-8601 timestamp (default: `false`).\n */\nexport function createConsoleTransport(options?: {\n colors?: boolean\n timestamp?: boolean\n}): Transport {\n const useColors = options?.colors !== false\n const showTimestamp = options?.timestamp ?? false\n\n return {\n log(level, message, meta) {\n const parts: string[] = []\n\n if (showTimestamp) {\n parts.push(new Date().toISOString())\n }\n\n if (useColors) {\n const color = LEVEL_COLORS[level]\n parts.push(`${color}[${level.toUpperCase()}]${RESET}`)\n } else {\n parts.push(`[${level.toUpperCase()}]`)\n }\n\n parts.push(message)\n\n if (meta !== undefined && Object.keys(meta).length > 0) {\n parts.push(JSON.stringify(meta))\n }\n\n console.log(parts.join(' '))\n },\n }\n}\n\n/**\n * Creates a transport that outputs structured JSON lines.\n *\n * Each log entry is serialized as a single JSON object with\n * `timestamp`, `level`, `message`, and optional `meta` fields.\n *\n * @param options - Configuration for the JSON transport.\n * @param options.stream - A writable stream (e.g. `process.stdout`).\n * Defaults to `process.stdout` in Node.js, falls\n * back to `console.log` in browsers.\n */\nexport function createJsonTransport(options?: {\n stream?: { write(data: string): void }\n}): Transport {\n const writeStream =\n options?.stream ??\n (typeof process !== 'undefined' &&\n typeof process.stdout !== 'undefined' &&\n typeof process.stdout.write === 'function'\n ? (process.stdout as { write(data: string): void })\n : undefined)\n\n return {\n log(level, message, meta) {\n const entry: Record<string, unknown> = {\n timestamp: new Date().toISOString(),\n level,\n message,\n }\n if (meta !== undefined && Object.keys(meta).length > 0) {\n entry.meta = meta\n }\n const line = JSON.stringify(entry)\n if (writeStream !== undefined) {\n writeStream.write(line + '\\n')\n } else {\n console.log(line)\n }\n },\n }\n}\n\n/**\n * Creates a transport that appends log entries to a file.\n *\n * Each line is formatted as: `[timestamp] [LEVEL] message {meta}`\n *\n * ⚠️ Node.js only. Silently discards log entries when `fs` is unavailable\n * (browsers, Deno, Bun — though Bun supports `fs`).\n *\n * @param filename - Path to the log file.\n * @param options - Configuration for the file transport.\n * @param options.maxSize - Maximum file size in bytes before rotation\n * (default: 10 MB). **Note:** rotation is not\n * yet implemented; this is reserved for future use.\n */\nexport function createFileTransport(\n filename: string,\n _options?: { maxSize?: number },\n): Transport {\n // Try to resolve fs synchronously at construction time\n // Uses process.versions.node as a heuristic for Node.js environment\n let fs: { appendFileSync: (path: string, data: string) => void } | null = null\n try {\n if (typeof process !== 'undefined' && process.versions?.node) {\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const m = require('fs') as { appendFileSync: (path: string, data: string) => void }\n fs = m\n }\n } catch {\n // fs unavailable (browser, edge runtime)\n }\n\n return {\n log(level, message, meta) {\n if (fs === null) return\n\n const metaStr =\n meta !== undefined && Object.keys(meta).length > 0 ? ` ${JSON.stringify(meta)}` : ''\n const line = `[${new Date().toISOString()}] [${level.toUpperCase()}] ${message}${metaStr}\\n`\n\n try {\n fs.appendFileSync(filename, line)\n } catch {\n // Silently drop if write fails (disk full, permissions, etc.)\n }\n },\n }\n}\n\n/**\n * Creates a buffered transport that batches log entries and flushes them\n * to the underlying transport when either the buffer size or flush interval\n * is reached (whichever comes first).\n *\n * Useful for reducing I/O pressure in high-throughput scenarios.\n *\n * @param transport - The underlying transport to flush to.\n * @param options - Configuration for the buffer.\n * @param options.maxSize - Maximum number of entries before forced flush\n * (default: 100).\n * @param options.flushIntervalMs - How often to auto-flush in milliseconds\n * (default: 5000). Set to `0` to disable\n * interval flushing.\n */\nexport function createBufferedTransport(\n transport: Transport,\n options?: { maxSize?: number; flushIntervalMs?: number },\n): Transport {\n const maxSize = options?.maxSize ?? 100\n const flushIntervalMs = options?.flushIntervalMs ?? 5000\n\n const buffer: Array<{\n level: LogLevel\n message: string\n meta?: Record<string, unknown>\n }> = []\n\n let timer: ReturnType<typeof setTimeout> | undefined\n\n function flush(): void {\n if (timer !== undefined) {\n clearTimeout(timer)\n timer = undefined\n }\n for (let i = 0; i < buffer.length; i++) {\n const entry = buffer[i]!\n transport.log(entry.level, entry.message, entry.meta)\n }\n buffer.length = 0\n }\n\n function scheduleFlush(): void {\n if (timer !== undefined || flushIntervalMs <= 0) return\n timer = setTimeout((): void => {\n timer = undefined\n flush()\n }, flushIntervalMs)\n }\n\n return {\n log(level, message, meta) {\n buffer.push({ level, message, meta })\n if (buffer.length >= maxSize) {\n flush()\n } else {\n scheduleFlush()\n }\n },\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/math/index.d.ts
CHANGED
|
@@ -113,5 +113,63 @@ declare function randomInt(min: number, max: number): number;
|
|
|
113
113
|
* @returns Whether the value is in range.
|
|
114
114
|
*/
|
|
115
115
|
declare function inRange(value: number, min: number, max: number): boolean;
|
|
116
|
+
/**
|
|
117
|
+
* Computes the median of an array of numbers.
|
|
118
|
+
*
|
|
119
|
+
* @param values - Array of numbers.
|
|
120
|
+
* @returns The median value.
|
|
121
|
+
* @throws {RangeError} If the array is empty.
|
|
122
|
+
*/
|
|
123
|
+
declare function median(values: number[]): number;
|
|
124
|
+
/**
|
|
125
|
+
* Computes the population standard deviation.
|
|
126
|
+
*
|
|
127
|
+
* @param values - Array of numbers.
|
|
128
|
+
* @returns The standard deviation.
|
|
129
|
+
* @throws {RangeError} If the array has fewer than 2 values.
|
|
130
|
+
*/
|
|
131
|
+
declare function stddev(values: number[]): number;
|
|
132
|
+
/**
|
|
133
|
+
* Computes the sample standard deviation (Bessel's correction).
|
|
134
|
+
*
|
|
135
|
+
* @param values - Array of numbers.
|
|
136
|
+
* @returns The sample standard deviation.
|
|
137
|
+
* @throws {RangeError} If the array has fewer than 2 values.
|
|
138
|
+
*/
|
|
139
|
+
declare function sampleStddev(values: number[]): number;
|
|
140
|
+
/**
|
|
141
|
+
* Computes the percentile value (0-100) using linear interpolation.
|
|
142
|
+
*
|
|
143
|
+
* @param values - Array of numbers.
|
|
144
|
+
* @param p - Percentile (0-100).
|
|
145
|
+
* @returns The percentile value.
|
|
146
|
+
* @throws {RangeError} If p is outside [0, 100] or array is empty.
|
|
147
|
+
*/
|
|
148
|
+
declare function percentile(values: number[], p: number): number;
|
|
149
|
+
/**
|
|
150
|
+
* Computes the Pearson correlation coefficient between two arrays.
|
|
151
|
+
*
|
|
152
|
+
* @param x - First array.
|
|
153
|
+
* @param y - Second array.
|
|
154
|
+
* @returns The correlation coefficient (-1 to 1).
|
|
155
|
+
* @throws {RangeError} If arrays have different lengths or fewer than 2 pairs.
|
|
156
|
+
*/
|
|
157
|
+
declare function correlation(x: number[], y: number[]): number;
|
|
158
|
+
/**
|
|
159
|
+
* Formats a number as a currency string with locale support.
|
|
160
|
+
*
|
|
161
|
+
* @example formatCurrency(1500000) // "Rp1.500.000"
|
|
162
|
+
* @example formatCurrency(1500000, { notation: 'compact' }) // "Rp1,5 jt"
|
|
163
|
+
* @example formatCurrency(99.99, { locale: 'en-US', currency: 'USD' }) // "$99.99"
|
|
164
|
+
*
|
|
165
|
+
* @param value - The number to format.
|
|
166
|
+
* @param options - Formatting options.
|
|
167
|
+
* @returns The formatted currency string.
|
|
168
|
+
*/
|
|
169
|
+
declare function formatCurrency(value: number, options?: {
|
|
170
|
+
locale?: string;
|
|
171
|
+
currency?: string;
|
|
172
|
+
notation?: 'standard' | 'compact';
|
|
173
|
+
}): string;
|
|
116
174
|
|
|
117
|
-
export { DivisionByZeroError, add, approxEqual, average, ceil, clamp, div, floor, inRange, mul, randomInt, round, sub, sum };
|
|
175
|
+
export { DivisionByZeroError, add, approxEqual, average, ceil, clamp, correlation, div, floor, formatCurrency, inRange, median, mul, percentile, randomInt, round, sampleStddev, stddev, sub, sum };
|
package/dist/math/index.js
CHANGED
|
@@ -86,6 +86,69 @@ function randomInt(min, max) {
|
|
|
86
86
|
function inRange(value, min, max) {
|
|
87
87
|
return value >= min && value <= max;
|
|
88
88
|
}
|
|
89
|
+
function median(values) {
|
|
90
|
+
if (values.length === 0) throw new RangeError("Cannot compute median of an empty array");
|
|
91
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
92
|
+
const mid = Math.floor(sorted.length / 2);
|
|
93
|
+
return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
94
|
+
}
|
|
95
|
+
function stddev(values) {
|
|
96
|
+
if (values.length < 2) throw new RangeError("Need at least 2 values for stddev");
|
|
97
|
+
const mean = sum(values) / values.length;
|
|
98
|
+
const sqDiffs = values.map((v) => (v - mean) ** 2);
|
|
99
|
+
return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / values.length);
|
|
100
|
+
}
|
|
101
|
+
function sampleStddev(values) {
|
|
102
|
+
if (values.length < 2) throw new RangeError("Need at least 2 values for sample stddev");
|
|
103
|
+
const mean = sum(values) / values.length;
|
|
104
|
+
const sqDiffs = values.map((v) => (v - mean) ** 2);
|
|
105
|
+
return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / (values.length - 1));
|
|
106
|
+
}
|
|
107
|
+
function percentile(values, p) {
|
|
108
|
+
if (values.length === 0) throw new RangeError("Cannot compute percentile of empty array");
|
|
109
|
+
if (p < 0 || p > 100) throw new RangeError("Percentile must be between 0 and 100");
|
|
110
|
+
const sorted = [...values].sort((a, b) => a - b);
|
|
111
|
+
const rank = p / 100 * (sorted.length - 1);
|
|
112
|
+
const lower = Math.floor(rank);
|
|
113
|
+
const upper = Math.ceil(rank);
|
|
114
|
+
if (lower === upper) return sorted[lower];
|
|
115
|
+
return sorted[lower] + (sorted[upper] - sorted[lower]) * (rank - lower);
|
|
116
|
+
}
|
|
117
|
+
function correlation(x, y) {
|
|
118
|
+
if (x.length !== y.length) throw new RangeError("Arrays must have the same length");
|
|
119
|
+
if (x.length < 2) throw new RangeError("Need at least 2 pairs for correlation");
|
|
120
|
+
const n = x.length;
|
|
121
|
+
const meanX = sum(x) / n;
|
|
122
|
+
const meanY = sum(y) / n;
|
|
123
|
+
let num = 0;
|
|
124
|
+
let denX = 0;
|
|
125
|
+
let denY = 0;
|
|
126
|
+
for (let i = 0; i < n; i++) {
|
|
127
|
+
const dx = x[i] - meanX;
|
|
128
|
+
const dy = y[i] - meanY;
|
|
129
|
+
num += dx * dy;
|
|
130
|
+
denX += dx * dx;
|
|
131
|
+
denY += dy * dy;
|
|
132
|
+
}
|
|
133
|
+
if (denX === 0 || denY === 0) return 0;
|
|
134
|
+
return num / Math.sqrt(denX * denY);
|
|
135
|
+
}
|
|
136
|
+
function formatCurrency(value, options) {
|
|
137
|
+
const locale = options?.locale ?? "id-ID";
|
|
138
|
+
const currency = options?.currency ?? "IDR";
|
|
139
|
+
const notation = options?.notation ?? "standard";
|
|
140
|
+
try {
|
|
141
|
+
return new Intl.NumberFormat(locale, {
|
|
142
|
+
style: "currency",
|
|
143
|
+
currency,
|
|
144
|
+
notation,
|
|
145
|
+
minimumFractionDigits: 0,
|
|
146
|
+
maximumFractionDigits: 2
|
|
147
|
+
}).format(value);
|
|
148
|
+
} catch {
|
|
149
|
+
return `${currency} ${value.toLocaleString(locale)}`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
89
152
|
export {
|
|
90
153
|
DivisionByZeroError,
|
|
91
154
|
add,
|
|
@@ -93,12 +156,18 @@ export {
|
|
|
93
156
|
average,
|
|
94
157
|
ceil,
|
|
95
158
|
clamp,
|
|
159
|
+
correlation,
|
|
96
160
|
div,
|
|
97
161
|
floor,
|
|
162
|
+
formatCurrency,
|
|
98
163
|
inRange,
|
|
164
|
+
median,
|
|
99
165
|
mul,
|
|
166
|
+
percentile,
|
|
100
167
|
randomInt,
|
|
101
168
|
round,
|
|
169
|
+
sampleStddev,
|
|
170
|
+
stddev,
|
|
102
171
|
sub,
|
|
103
172
|
sum
|
|
104
173
|
};
|
package/dist/math/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/math/index.ts"],"sourcesContent":["/**\r\n * Error thrown when attempting to divide by zero.\r\n */\r\nexport class DivisionByZeroError extends Error {\r\n constructor() {\r\n super('Division by zero')\r\n this.name = 'DivisionByZeroError'\r\n }\r\n}\r\n\r\nfunction getPrecision(value: number): number {\r\n if (!isFinite(value)) return 0\r\n const eIndex = String(value).indexOf('e')\r\n if (eIndex > -1) {\r\n const exp = parseInt(String(value).slice(eIndex + 1), 10)\r\n if (exp < 0) return Math.abs(exp)\r\n return 0\r\n }\r\n const str = String(value)\r\n const dot = str.indexOf('.')\r\n return dot === -1 ? 0 : str.length - dot - 1\r\n}\r\n\r\nfunction toPrecisionFactor(a: number, b: number): number {\r\n return Math.pow(10, Math.max(getPrecision(a), getPrecision(b)))\r\n}\r\n\r\n/**\r\n * Safely adds two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The sum.\r\n */\r\nexport function add(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) + Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely subtracts two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The difference.\r\n */\r\nexport function sub(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) - Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely multiplies two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The product.\r\n */\r\nexport function mul(a: number, b: number): number {\r\n const factorA = toPrecisionFactor(a, 1)\r\n const factorB = toPrecisionFactor(1, b)\r\n const result = (Math.round(a * factorA) * Math.round(b * factorB)) / (factorA * factorB)\r\n return result\r\n}\r\n\r\n/**\r\n * Safely divides two numbers.\r\n *\r\n * @param a - The dividend.\r\n * @param b - The divisor.\r\n * @returns The quotient.\r\n * @throws {DivisionByZeroError} If `b` is zero.\r\n */\r\nexport function div(a: number, b: number): number {\r\n if (b === 0) throw new DivisionByZeroError()\r\n const factor = toPrecisionFactor(a, b)\r\n return Math.round(a * factor) / Math.round(b * factor)\r\n}\r\n\r\n/**\r\n * Rounds a number to the given precision.\r\n *\r\n * @param value - The number to round.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The rounded value.\r\n */\r\nexport function round(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n // Use toPrecision to avoid floating-point multiplication errors\r\n // e.g. 1.005 * 100 = 100.49999999999999 without this fix\r\n const shifted = Number((value * factor).toPrecision(15))\r\n return Math.round(shifted) / factor\r\n}\r\n\r\n/**\r\n * Floors a number to the given precision.\r\n *\r\n * @param value - The number to floor.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The floored value.\r\n */\r\nexport function floor(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.floor(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Ceils a number to the given precision.\r\n *\r\n * @param value - The number to ceil.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The ceiled value.\r\n */\r\nexport function ceil(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.ceil(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Checks if two numbers are approximately equal within a tolerance.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @param tolerance - Maximum difference (default `Number.EPSILON`).\r\n * @returns Whether the numbers are approximately equal.\r\n */\r\nexport function approxEqual(a: number, b: number, tolerance: number = Number.EPSILON): boolean {\r\n return Math.abs(a - b) <= tolerance\r\n}\r\n\r\n/**\r\n * Clamps a value within the inclusive range [min, max].\r\n *\r\n * @param value - The value to clamp.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns The clamped value.\r\n * @throws {RangeError} If `min` exceeds `max`.\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.min(Math.max(value, min), max)\r\n}\r\n\r\n/**\r\n * Computes the sum of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The total sum.\r\n */\r\nexport function sum(values: number[]): number {\r\n let total = 0\r\n for (let i = 0; i < values.length; i++) {\r\n total += values[i]!\r\n }\r\n return total\r\n}\r\n\r\n/**\r\n * Computes the average (mean) of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The average.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function average(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute average of an empty array')\r\n }\r\n return sum(values) / values.length\r\n}\r\n\r\n/**\r\n * Generates a random integer between `min` and `max` (inclusive).\r\n *\r\n * @param min - The minimum integer.\r\n * @param max - The maximum integer.\r\n * @returns A random integer.\r\n * @throws {RangeError} If arguments are not integers or `min > max`.\r\n */\r\nexport function randomInt(min: number, max: number): number {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.floor(Math.random() * (max - min + 1)) + min\r\n}\r\n\r\n/**\r\n * Checks if a number is within the inclusive range [min, max].\r\n *\r\n * @param value - The number to check.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns Whether the value is in range.\r\n */\r\nexport function inRange(value: number, min: number, max: number): boolean {\r\n return value >= min && value <= max\r\n}\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/math/index.ts"],"sourcesContent":["/**\r\n * Error thrown when attempting to divide by zero.\r\n */\r\nexport class DivisionByZeroError extends Error {\r\n constructor() {\r\n super('Division by zero')\r\n this.name = 'DivisionByZeroError'\r\n }\r\n}\r\n\r\nfunction getPrecision(value: number): number {\r\n if (!isFinite(value)) return 0\r\n const eIndex = String(value).indexOf('e')\r\n if (eIndex > -1) {\r\n const exp = parseInt(String(value).slice(eIndex + 1), 10)\r\n if (exp < 0) return Math.abs(exp)\r\n return 0\r\n }\r\n const str = String(value)\r\n const dot = str.indexOf('.')\r\n return dot === -1 ? 0 : str.length - dot - 1\r\n}\r\n\r\nfunction toPrecisionFactor(a: number, b: number): number {\r\n return Math.pow(10, Math.max(getPrecision(a), getPrecision(b)))\r\n}\r\n\r\n/**\r\n * Safely adds two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The sum.\r\n */\r\nexport function add(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) + Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely subtracts two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The difference.\r\n */\r\nexport function sub(a: number, b: number): number {\r\n const factor = toPrecisionFactor(a, b)\r\n return (Math.round(a * factor) - Math.round(b * factor)) / factor\r\n}\r\n\r\n/**\r\n * Safely multiplies two numbers, handling floating-point precision.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @returns The product.\r\n */\r\nexport function mul(a: number, b: number): number {\r\n const factorA = toPrecisionFactor(a, 1)\r\n const factorB = toPrecisionFactor(1, b)\r\n const result = (Math.round(a * factorA) * Math.round(b * factorB)) / (factorA * factorB)\r\n return result\r\n}\r\n\r\n/**\r\n * Safely divides two numbers.\r\n *\r\n * @param a - The dividend.\r\n * @param b - The divisor.\r\n * @returns The quotient.\r\n * @throws {DivisionByZeroError} If `b` is zero.\r\n */\r\nexport function div(a: number, b: number): number {\r\n if (b === 0) throw new DivisionByZeroError()\r\n const factor = toPrecisionFactor(a, b)\r\n return Math.round(a * factor) / Math.round(b * factor)\r\n}\r\n\r\n/**\r\n * Rounds a number to the given precision.\r\n *\r\n * @param value - The number to round.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The rounded value.\r\n */\r\nexport function round(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n // Use toPrecision to avoid floating-point multiplication errors\r\n // e.g. 1.005 * 100 = 100.49999999999999 without this fix\r\n const shifted = Number((value * factor).toPrecision(15))\r\n return Math.round(shifted) / factor\r\n}\r\n\r\n/**\r\n * Floors a number to the given precision.\r\n *\r\n * @param value - The number to floor.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The floored value.\r\n */\r\nexport function floor(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.floor(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Ceils a number to the given precision.\r\n *\r\n * @param value - The number to ceil.\r\n * @param precision - Number of decimal places (default 0).\r\n * @returns The ceiled value.\r\n */\r\nexport function ceil(value: number, precision: number = 0): number {\r\n const factor = Math.pow(10, precision)\r\n return Math.ceil(value * factor) / factor\r\n}\r\n\r\n/**\r\n * Checks if two numbers are approximately equal within a tolerance.\r\n *\r\n * @param a - First number.\r\n * @param b - Second number.\r\n * @param tolerance - Maximum difference (default `Number.EPSILON`).\r\n * @returns Whether the numbers are approximately equal.\r\n */\r\nexport function approxEqual(a: number, b: number, tolerance: number = Number.EPSILON): boolean {\r\n return Math.abs(a - b) <= tolerance\r\n}\r\n\r\n/**\r\n * Clamps a value within the inclusive range [min, max].\r\n *\r\n * @param value - The value to clamp.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns The clamped value.\r\n * @throws {RangeError} If `min` exceeds `max`.\r\n */\r\nexport function clamp(value: number, min: number, max: number): number {\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.min(Math.max(value, min), max)\r\n}\r\n\r\n/**\r\n * Computes the sum of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The total sum.\r\n */\r\nexport function sum(values: number[]): number {\r\n let total = 0\r\n for (let i = 0; i < values.length; i++) {\r\n total += values[i]!\r\n }\r\n return total\r\n}\r\n\r\n/**\r\n * Computes the average (mean) of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The average.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function average(values: number[]): number {\r\n if (values.length === 0) {\r\n throw new RangeError('Cannot compute average of an empty array')\r\n }\r\n return sum(values) / values.length\r\n}\r\n\r\n/**\r\n * Generates a random integer between `min` and `max` (inclusive).\r\n *\r\n * @param min - The minimum integer.\r\n * @param max - The maximum integer.\r\n * @returns A random integer.\r\n * @throws {RangeError} If arguments are not integers or `min > max`.\r\n */\r\nexport function randomInt(min: number, max: number): number {\r\n if (!Number.isInteger(min) || !Number.isInteger(max)) {\r\n throw new RangeError('Arguments must be integers')\r\n }\r\n if (min > max) {\r\n throw new RangeError('Minimum value cannot exceed maximum value')\r\n }\r\n return Math.floor(Math.random() * (max - min + 1)) + min\r\n}\r\n\r\n/**\r\n * Checks if a number is within the inclusive range [min, max].\r\n *\r\n * @param value - The number to check.\r\n * @param min - The lower bound.\r\n * @param max - The upper bound.\r\n * @returns Whether the value is in range.\r\n */\r\nexport function inRange(value: number, min: number, max: number): boolean {\r\n return value >= min && value <= max\r\n}\r\n\r\n// ─── Statistics ─────────────────────────────────────────\r\n\r\n/**\r\n * Computes the median of an array of numbers.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The median value.\r\n * @throws {RangeError} If the array is empty.\r\n */\r\nexport function median(values: number[]): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute median of an empty array')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const mid = Math.floor(sorted.length / 2)\r\n return sorted.length % 2 === 0 ? (sorted[mid - 1]! + sorted[mid]!) / 2 : sorted[mid]!\r\n}\r\n\r\n/**\r\n * Computes the population standard deviation.\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function stddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / values.length)\r\n}\r\n\r\n/**\r\n * Computes the sample standard deviation (Bessel's correction).\r\n *\r\n * @param values - Array of numbers.\r\n * @returns The sample standard deviation.\r\n * @throws {RangeError} If the array has fewer than 2 values.\r\n */\r\nexport function sampleStddev(values: number[]): number {\r\n if (values.length < 2) throw new RangeError('Need at least 2 values for sample stddev')\r\n const mean = sum(values) / values.length\r\n const sqDiffs = values.map((v) => (v - mean) ** 2)\r\n return Math.sqrt(sqDiffs.reduce((a, b) => a + b, 0) / (values.length - 1))\r\n}\r\n\r\n/**\r\n * Computes the percentile value (0-100) using linear interpolation.\r\n *\r\n * @param values - Array of numbers.\r\n * @param p - Percentile (0-100).\r\n * @returns The percentile value.\r\n * @throws {RangeError} If p is outside [0, 100] or array is empty.\r\n */\r\nexport function percentile(values: number[], p: number): number {\r\n if (values.length === 0) throw new RangeError('Cannot compute percentile of empty array')\r\n if (p < 0 || p > 100) throw new RangeError('Percentile must be between 0 and 100')\r\n const sorted = [...values].sort((a, b) => a - b)\r\n const rank = (p / 100) * (sorted.length - 1)\r\n const lower = Math.floor(rank)\r\n const upper = Math.ceil(rank)\r\n if (lower === upper) return sorted[lower]!\r\n return sorted[lower]! + (sorted[upper]! - sorted[lower]!) * (rank - lower)\r\n}\r\n\r\n/**\r\n * Computes the Pearson correlation coefficient between two arrays.\r\n *\r\n * @param x - First array.\r\n * @param y - Second array.\r\n * @returns The correlation coefficient (-1 to 1).\r\n * @throws {RangeError} If arrays have different lengths or fewer than 2 pairs.\r\n */\r\nexport function correlation(x: number[], y: number[]): number {\r\n if (x.length !== y.length) throw new RangeError('Arrays must have the same length')\r\n if (x.length < 2) throw new RangeError('Need at least 2 pairs for correlation')\r\n const n = x.length\r\n const meanX = sum(x) / n\r\n const meanY = sum(y) / n\r\n let num = 0\r\n let denX = 0\r\n let denY = 0\r\n for (let i = 0; i < n; i++) {\r\n const dx = x[i]! - meanX\r\n const dy = y[i]! - meanY\r\n num += dx * dy\r\n denX += dx * dx\r\n denY += dy * dy\r\n }\r\n if (denX === 0 || denY === 0) return 0\r\n return num / Math.sqrt(denX * denY)\r\n}\r\n\r\n/**\r\n * Formats a number as a currency string with locale support.\r\n *\r\n * @example formatCurrency(1500000) // \"Rp1.500.000\"\r\n * @example formatCurrency(1500000, { notation: 'compact' }) // \"Rp1,5 jt\"\r\n * @example formatCurrency(99.99, { locale: 'en-US', currency: 'USD' }) // \"$99.99\"\r\n *\r\n * @param value - The number to format.\r\n * @param options - Formatting options.\r\n * @returns The formatted currency string.\r\n */\r\nexport function formatCurrency(\r\n value: number,\r\n options?: { locale?: string; currency?: string; notation?: 'standard' | 'compact' },\r\n): string {\r\n const locale = options?.locale ?? 'id-ID'\r\n const currency = options?.currency ?? 'IDR'\r\n const notation = options?.notation ?? 'standard'\r\n\r\n try {\r\n return new Intl.NumberFormat(locale, {\r\n style: 'currency',\r\n currency,\r\n notation,\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 2,\r\n }).format(value)\r\n } catch {\r\n return `${currency} ${value.toLocaleString(locale)}`\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
package/dist/string/index.d.ts
CHANGED
|
@@ -87,5 +87,45 @@ declare function slugify(str: string): string;
|
|
|
87
87
|
* Counts occurrences of a substring in a string.
|
|
88
88
|
*/
|
|
89
89
|
declare function countOccurrences(str: string, substring: string): number;
|
|
90
|
+
/**
|
|
91
|
+
* Computes the Levenshtein distance between two strings.
|
|
92
|
+
* Uses iterative DP with O(min(m,n)) space.
|
|
93
|
+
*/
|
|
94
|
+
declare function levenshtein(a: string, b: string): number;
|
|
95
|
+
/**
|
|
96
|
+
* Performs a simple fuzzy match: checks if all characters of query
|
|
97
|
+
* appear in str in order (case-insensitive).
|
|
98
|
+
*/
|
|
99
|
+
declare function fuzzyMatch(str: string, query: string): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Masks parts of a string, useful for data compliance (PDPA/GDPR).
|
|
102
|
+
*
|
|
103
|
+
* @example maskString('08123456789') // "0812****789"
|
|
104
|
+
* @example maskString('hello@email.com') // "h***@e***.com"
|
|
105
|
+
* @example maskString('1234567890', { start: 0, end: 4, char: '#' }) // "####567890"
|
|
106
|
+
*/
|
|
107
|
+
declare function maskString(str: string, options?: {
|
|
108
|
+
start?: number;
|
|
109
|
+
end?: number;
|
|
110
|
+
char?: string;
|
|
111
|
+
}): string;
|
|
112
|
+
/**
|
|
113
|
+
* Converts a number to Indonesian words (terbilang).
|
|
114
|
+
*
|
|
115
|
+
* @example terbilang(1500000) // "satu juta lima ratus ribu"
|
|
116
|
+
* @example terbilang(2024) // "dua ribu dua puluh empat"
|
|
117
|
+
* @example terbilang(11) // "sebelas"
|
|
118
|
+
* @example terbilang(100) // "seratus"
|
|
119
|
+
*/
|
|
120
|
+
declare function terbilang(value: number): string;
|
|
121
|
+
/**
|
|
122
|
+
* Formats a number as Indonesian Rupiah string.
|
|
123
|
+
*
|
|
124
|
+
* @example formatRupiah(1500000) // "Rp1.500.000"
|
|
125
|
+
* @example formatRupiah(1500000, { notation: 'compact' }) // "Rp1,5 jt"
|
|
126
|
+
*/
|
|
127
|
+
declare function formatRupiah(value: number, options?: {
|
|
128
|
+
notation?: 'standard' | 'compact';
|
|
129
|
+
}): string;
|
|
90
130
|
|
|
91
|
-
export { camelCase, capitalize, countOccurrences, escapeHtml, kebabCase, nanoid, pad, padEnd, padStart, pascalCase, reverse, slugify, snakeCase, template, trim, trimEnd, trimStart, truncate, unescapeHtml, uuid, words };
|
|
131
|
+
export { camelCase, capitalize, countOccurrences, escapeHtml, formatRupiah, fuzzyMatch, kebabCase, levenshtein, maskString, nanoid, pad, padEnd, padStart, pascalCase, reverse, slugify, snakeCase, template, terbilang, trim, trimEnd, trimStart, truncate, unescapeHtml, uuid, words };
|