@ix-xs/node-comfort 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +32 -30
  3. package/index.js +3 -3
  4. package/package.json +8 -7
  5. package/src/Arr.js +222 -58
  6. package/src/Async.js +322 -90
  7. package/src/Cache.js +148 -44
  8. package/src/Checker.js +257 -26
  9. package/src/Cli.js +404 -145
  10. package/src/Color.js +224 -83
  11. package/src/Crypto.js +481 -106
  12. package/src/Emitter.js +104 -37
  13. package/src/Env.js +176 -42
  14. package/src/FS.js +203 -60
  15. package/src/Func.js +226 -76
  16. package/src/Http.js +311 -149
  17. package/src/Id.js +281 -96
  18. package/src/Logger.js +302 -78
  19. package/src/Num.js +182 -60
  20. package/src/Obj.js +340 -125
  21. package/src/SQLite.js +426 -105
  22. package/src/Schema.js +602 -87
  23. package/src/Str.js +316 -86
  24. package/src/Sys.js +197 -54
  25. package/src/Time.js +572 -317
  26. package/src/Utils.js +62 -41
  27. package/src/errors.js +54 -11
  28. package/src/internal/shared.js +415 -0
  29. package/types/index.d.ts +59 -17
  30. package/types/src/Arr.d.ts +78 -0
  31. package/types/src/Arr.d.ts.map +1 -1
  32. package/types/src/Async.d.ts +52 -1
  33. package/types/src/Async.d.ts.map +1 -1
  34. package/types/src/Cache.d.ts +33 -7
  35. package/types/src/Cache.d.ts.map +1 -1
  36. package/types/src/Checker.d.ts +155 -2
  37. package/types/src/Checker.d.ts.map +1 -1
  38. package/types/src/Cli.d.ts +97 -15
  39. package/types/src/Cli.d.ts.map +1 -1
  40. package/types/src/Color.d.ts +7 -1
  41. package/types/src/Color.d.ts.map +1 -1
  42. package/types/src/Crypto.d.ts +99 -13
  43. package/types/src/Crypto.d.ts.map +1 -1
  44. package/types/src/Emitter.d.ts +36 -17
  45. package/types/src/Emitter.d.ts.map +1 -1
  46. package/types/src/Env.d.ts +31 -1
  47. package/types/src/Env.d.ts.map +1 -1
  48. package/types/src/FS.d.ts +36 -5
  49. package/types/src/FS.d.ts.map +1 -1
  50. package/types/src/Func.d.ts +147 -19
  51. package/types/src/Func.d.ts.map +1 -1
  52. package/types/src/Http.d.ts +113 -43
  53. package/types/src/Http.d.ts.map +1 -1
  54. package/types/src/Id.d.ts +60 -8
  55. package/types/src/Id.d.ts.map +1 -1
  56. package/types/src/Logger.d.ts +53 -2
  57. package/types/src/Logger.d.ts.map +1 -1
  58. package/types/src/Num.d.ts +48 -7
  59. package/types/src/Num.d.ts.map +1 -1
  60. package/types/src/Obj.d.ts +67 -4
  61. package/types/src/Obj.d.ts.map +1 -1
  62. package/types/src/SQLite.d.ts +123 -14
  63. package/types/src/SQLite.d.ts.map +1 -1
  64. package/types/src/Schema.d.ts +256 -7
  65. package/types/src/Schema.d.ts.map +1 -1
  66. package/types/src/Str.d.ts +49 -1
  67. package/types/src/Str.d.ts.map +1 -1
  68. package/types/src/Sys.d.ts +51 -6
  69. package/types/src/Sys.d.ts.map +1 -1
  70. package/types/src/Time.d.ts +130 -34
  71. package/types/src/Time.d.ts.map +1 -1
  72. package/types/src/Utils.d.ts +15 -11
  73. package/types/src/Utils.d.ts.map +1 -1
  74. package/types/src/errors.d.ts +27 -3
  75. package/types/src/errors.d.ts.map +1 -1
  76. package/types/src/internal/shared.d.ts +123 -0
  77. package/types/src/internal/shared.d.ts.map +1 -0
@@ -0,0 +1,415 @@
1
+ "use strict";
2
+
3
+ // Helpers shared by the modules. Not part of the public API.
4
+
5
+ const { types } = require("node:util");
6
+
7
+ /** Keys that must never be copied onto an object: they reach its prototype. */
8
+ const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
9
+
10
+ /**
11
+ * @param {unknown} value
12
+ * @returns {value is Record<string, any>}
13
+ */
14
+ const isPlainObject = (value) => {
15
+ if (typeof value !== "object" || value === null) return false;
16
+ const proto = Object.getPrototypeOf(value);
17
+ // The last check accepts objects from another realm (a worker, a `vm`).
18
+ return proto === null || proto === Object.prototype || Object.getPrototypeOf(proto) === null;
19
+ };
20
+
21
+ const MS = { ms: 1, s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000, w: 604_800_000 };
22
+
23
+ /**
24
+ * The defaults set with `time.setLocale()` and `time.setTimezone()`. English
25
+ * by default, so output never depends on the machine.
26
+ * @type {{ locale: string, timeZone: string | undefined }}
27
+ */
28
+ const timeConfig = { locale: "en", timeZone: undefined };
29
+
30
+ /** @type {Record<string, keyof typeof MS | "M" | "y">} */
31
+ const UNIT_ALIASES = {
32
+ ms: "ms", msec: "ms", milli: "ms", millis: "ms", millisecond: "ms", milliseconds: "ms",
33
+ s: "s", sec: "s", secs: "s", second: "s", seconds: "s",
34
+ m: "m", min: "m", mins: "m", minute: "m", minutes: "m",
35
+ h: "h", hr: "h", hrs: "h", hour: "h", hours: "h",
36
+ d: "d", day: "d", days: "d",
37
+ w: "w", wk: "w", wks: "w", week: "w", weeks: "w",
38
+ M: "M", mo: "M", month: "M", months: "M",
39
+ y: "y", yr: "y", yrs: "y", year: "y", years: "y",
40
+ };
41
+
42
+ /**
43
+ * @param {string} unit
44
+ * @returns {keyof typeof MS | "M" | "y" | undefined}
45
+ */
46
+ const unitOf = (unit) => UNIT_ALIASES[unit] ?? UNIT_ALIASES[unit.toLowerCase()];
47
+
48
+ /** @type {Map<string, number | null>} */
49
+ const _durations = new Map();
50
+
51
+ /**
52
+ * @param {string} text Already trimmed, not empty.
53
+ * @returns {number | null}
54
+ */
55
+ const _readDuration = (text) => {
56
+ if (/^[-+]?\d+(?:\.\d+)?$/.test(text)) return Number(text);
57
+
58
+ const iso = /^([-+])?P(?:(\d+(?:[.,]\d+)?)W)?(?:(\d+(?:[.,]\d+)?)D)?(?:T(?:(\d+(?:[.,]\d+)?)H)?(?:(\d+(?:[.,]\d+)?)M)?(?:(\d+(?:[.,]\d+)?)S)?)?$/i.exec(text);
59
+ if (iso && /\d/.test(text)) {
60
+ const [, sign, w, d, h, m, s] = iso;
61
+ const n = (/** @type {string | undefined} */ v) => (v ? Number(v.replace(",", ".")) : 0);
62
+ const total = n(w) * MS.w + n(d) * MS.d + n(h) * MS.h + n(m) * MS.m + n(s) * MS.s;
63
+ return sign === "-" ? -total : total;
64
+ }
65
+
66
+ const clock = /^([-+])?(?:(\d+):)?(\d+):(\d{1,2})(?:\.(\d{1,3}))?$/.exec(text);
67
+ if (clock) {
68
+ const [, sign, h, m, s, ms] = clock;
69
+ const total = Number(h ?? 0) * MS.h + Number(m) * MS.m + Number(s) * MS.s + (ms ? Number(ms.padEnd(3, "0")) : 0);
70
+ return sign === "-" ? -total : total;
71
+ }
72
+
73
+ const regex = /(-?\d*\.?\d+)\s*([a-zA-Z]+)/g;
74
+ let total = 0;
75
+ let consumed = "";
76
+ let match;
77
+ while ((match = regex.exec(text)) !== null) {
78
+ const unit = unitOf(match[2]);
79
+ if (!unit || unit === "M" || unit === "y") return null;
80
+ total += Number(match[1]) * MS[unit];
81
+ consumed += match[0];
82
+ }
83
+ if (!consumed || consumed.replace(/\s+/g, "") !== text.replace(/[\s,]+|and/g, "")) return null;
84
+ return total;
85
+ };
86
+
87
+ /**
88
+ * A duration in milliseconds, or `null` when the input isn't one.
89
+ * @param {unknown} input
90
+ * @returns {number | null}
91
+ */
92
+ const parseDuration = (input) => {
93
+ if (typeof input === "number") return Number.isFinite(input) ? input : null;
94
+ if (typeof input !== "string") return null;
95
+ const cached = _durations.get(input);
96
+ if (cached !== undefined) return cached;
97
+ const text = input.trim();
98
+ const result = text ? _readDuration(text) : null;
99
+ // Durations are almost always literals, so a handful of entries covers a whole program.
100
+ if (_durations.size >= 500) _durations.clear();
101
+ _durations.set(input, result);
102
+ return result;
103
+ };
104
+
105
+ /**
106
+ * Milliseconds from a number or a duration string.
107
+ * @param {number | string} value
108
+ * @param {string} [what="duration"] Names the value in the error message.
109
+ * @returns {number}
110
+ * @throws {TypeError} If it can't be read.
111
+ */
112
+ const toMs = (value, what = "duration") => {
113
+ if (typeof value === "number") {
114
+ if (Number.isNaN(value)) throw new TypeError(`Invalid ${what}: NaN`);
115
+ return value;
116
+ }
117
+ const ms = parseDuration(value);
118
+ if (ms === null) throw new TypeError(`Invalid ${what}: ${JSON.stringify(value) ?? String(value)}`);
119
+ return ms;
120
+ };
121
+
122
+ const MAX_TIMEOUT = 2 ** 31 - 1;
123
+
124
+ /**
125
+ * `setTimeout` that also works past its 24.8-day limit. `Infinity` never fires.
126
+ * @param {() => void} fn
127
+ * @param {number} ms
128
+ * @param {boolean} [unref=false]
129
+ * @returns {() => void} Cancels the timer.
130
+ */
131
+ const setLongTimeout = (fn, ms, unref = false) => {
132
+ if (ms === Infinity) return () => {};
133
+ /** @type {NodeJS.Timeout | undefined} */
134
+ let timer;
135
+ const schedule = (/** @type {number} */ remaining) => {
136
+ timer = setTimeout(() => (remaining > MAX_TIMEOUT ? schedule(remaining - MAX_TIMEOUT) : fn()), Math.min(remaining, MAX_TIMEOUT));
137
+ if (unref) timer.unref();
138
+ };
139
+ schedule(Math.max(0, ms) || 0);
140
+ return () => clearTimeout(timer);
141
+ };
142
+
143
+ /**
144
+ * Resolves after `ms`, or rejects with an `AbortError` when the signal fires.
145
+ * @param {number} ms
146
+ * @param {AbortSignal} [signal]
147
+ * @param {boolean} [unref=false]
148
+ * @returns {Promise<void>}
149
+ */
150
+ const sleep = (ms, signal, unref = false) =>
151
+ new Promise((resolve, reject) => {
152
+ const { AbortError } = require("../errors.js");
153
+ if (signal?.aborted) return reject(new AbortError(undefined, { cause: signal.reason }));
154
+ const onAbort = () => {
155
+ cancel();
156
+ reject(new AbortError(undefined, { cause: signal?.reason }));
157
+ };
158
+ const cancel = setLongTimeout(() => {
159
+ signal?.removeEventListener("abort", onAbort);
160
+ resolve();
161
+ }, ms, unref);
162
+ signal?.addEventListener("abort", onAbort, { once: true });
163
+ });
164
+
165
+ /**
166
+ * Builds a cache of `Intl` objects, which are slow to construct (tens of
167
+ * microseconds) and fast to use.
168
+ * @template T
169
+ * @param {(locale: string | undefined, options: any) => T} create
170
+ * @returns {(locale?: string, options?: object) => T}
171
+ */
172
+ const _intlCache = (create) => {
173
+ /** @type {Map<string, T>} */
174
+ const cache = new Map();
175
+ return (locale, options) => {
176
+ const key = options === undefined ? `${locale ?? ""}` : `${locale ?? ""}|${JSON.stringify(options)}`;
177
+ let found = cache.get(key);
178
+ if (found === undefined) {
179
+ found = create(locale, options);
180
+ if (cache.size >= 200) cache.clear();
181
+ cache.set(key, found);
182
+ }
183
+ return found;
184
+ };
185
+ };
186
+
187
+ const intl = {
188
+ number: _intlCache((locale, options) => new Intl.NumberFormat(locale, options)),
189
+ dateTime: _intlCache((locale, options) => new Intl.DateTimeFormat(locale, options)),
190
+ plural: _intlCache((locale, options) => new Intl.PluralRules(locale, options)),
191
+ collator: _intlCache((locale, options) => new Intl.Collator(locale, options)),
192
+ relativeTime: _intlCache((locale, options) => new Intl.RelativeTimeFormat(locale, options)),
193
+ list: _intlCache((locale, options) => new Intl.ListFormat(locale, options)),
194
+ };
195
+
196
+ /** @type {Intl.Segmenter | undefined} */
197
+ let _segmenter;
198
+
199
+ /** The grapheme segmenter, built on first use. */
200
+ const segmenter = () => (_segmenter ??= new Intl.Segmenter(undefined, { granularity: "grapheme" }));
201
+
202
+ const ASCII = /^[\x00-\x7f]*$/;
203
+
204
+ /**
205
+ * Splits text into graphemes, so emoji and accents stay whole.
206
+ * @param {string} input
207
+ * @returns {string[]}
208
+ */
209
+ const graphemes = (input) => {
210
+ if (ASCII.test(input)) return input.split("");
211
+ return Array.from(segmenter().segment(input), (s) => s.segment);
212
+ };
213
+
214
+ /**
215
+ * Counts graphemes without building an array.
216
+ * @param {string} input
217
+ * @returns {number}
218
+ */
219
+ const graphemeCount = (input) => {
220
+ if (ASCII.test(input)) return input.length;
221
+ let count = 0;
222
+ for (const _ of segmenter().segment(input)) count++;
223
+ return count;
224
+ };
225
+
226
+ /**
227
+ * Adds numbers with Neumaier's compensation, so `0.1 + 0.2 + 0.3` is `0.6`.
228
+ * @param {Iterable<number>} values
229
+ * @returns {number}
230
+ */
231
+ const preciseSum = (values) => {
232
+ let sum = 0;
233
+ let compensation = 0;
234
+ for (const value of values) {
235
+ const next = sum + value;
236
+ compensation += Math.abs(sum) >= Math.abs(value) ? sum - next + value : value - next + sum;
237
+ sum = next;
238
+ }
239
+ const total = sum + compensation;
240
+ return Number.isNaN(total) ? sum : total;
241
+ };
242
+
243
+ /** @type {WeakMap<object, number>} */
244
+ const _objectIds = new WeakMap();
245
+ /** @type {Map<symbol, number>} */
246
+ const _symbolIds = new Map();
247
+ let _nextId = 0;
248
+
249
+ /** @param {object} value */
250
+ const _identity = (value) => {
251
+ let id = _objectIds.get(value);
252
+ if (id === undefined) _objectIds.set(value, (id = ++_nextId));
253
+ return id;
254
+ };
255
+
256
+ /**
257
+ * @param {unknown} value
258
+ * @returns {string}
259
+ */
260
+ const _keyPart = (value) => {
261
+ switch (typeof value) {
262
+ case "string": return `s${value.length}:${value}`;
263
+ case "number": return Object.is(value, -0) ? "n-0" : `n${value}`;
264
+ case "boolean": return value ? "t" : "f";
265
+ case "undefined": return "u";
266
+ case "bigint": return `i${value}`;
267
+ case "symbol": {
268
+ let id = _symbolIds.get(value);
269
+ if (id === undefined) _symbolIds.set(value, (id = ++_nextId));
270
+ return `y${id}`;
271
+ }
272
+ case "function": return `F${_identity(value)}`;
273
+ default: {
274
+ if (value === null) return "l";
275
+ const object = /** @type {object} */ (value);
276
+ if (types.isDate(object)) return `d${object.getTime()}`;
277
+ if (!Array.isArray(object) && !isPlainObject(object)) return `O${_identity(object)}`;
278
+ try {
279
+ // The replacer keeps what JSON drops or refuses: undefined, bigint, functions, symbols.
280
+ return `j${JSON.stringify(object, (_, v) => (v !== null && typeof v === "object" ? v : typeof v === "string" || typeof v === "number" || typeof v === "boolean" ? v : _keyPart(v)))}`;
281
+ } catch {
282
+ return `O${_identity(object)}`;
283
+ }
284
+ }
285
+ }
286
+ };
287
+
288
+ /**
289
+ * A cache key for a list of arguments. Primitives are compared by value and
290
+ * type, plain objects and arrays by content, everything else by identity.
291
+ * @param {readonly unknown[]} args
292
+ * @returns {unknown} A primitive usable as a `Map` key.
293
+ */
294
+ const argsKey = (args) => {
295
+ if (args.length === 1) {
296
+ const only = args[0];
297
+ // Numbers and booleans can't clash with the tagged strings used for everything else.
298
+ return typeof only === "number" || typeof only === "boolean" ? only : _keyPart(only);
299
+ }
300
+ if (args.length === 0) return "";
301
+ let key = "";
302
+ for (let i = 0; i < args.length; i++) key += `${_keyPart(args[i])}\u0001`;
303
+ return key;
304
+ };
305
+
306
+ /**
307
+ * Parsed cron fields. `lastDay` is set by `L` in the day-of-month field.
308
+ * @typedef {{ seconds: Set<number>, minutes: Set<number>, hours: Set<number>, days: Set<number>, months: Set<number>, weekdays: Set<number>, anyDay: boolean, anyWeekday: boolean, lastDay: boolean }} CronSpec
309
+ */
310
+
311
+ /** @type {Record<string, string>} */
312
+ const CRON_MACROS = {
313
+ "@yearly": "0 0 1 1 *", "@annually": "0 0 1 1 *", "@monthly": "0 0 1 * *", "@weekly": "0 0 * * 0",
314
+ "@daily": "0 0 * * *", "@midnight": "0 0 * * *", "@hourly": "0 * * * *",
315
+ };
316
+ const MONTH_NAMES = ["jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"];
317
+ const DAY_NAMES = ["sun", "mon", "tue", "wed", "thu", "fri", "sat"];
318
+
319
+ /** @type {Map<string, CronSpec>} */
320
+ const _cronSpecs = new Map();
321
+
322
+ /**
323
+ * Parses a cron expression with 5 fields, or 6 with seconds first.
324
+ * @param {string} expression
325
+ * @returns {CronSpec}
326
+ * @throws {SyntaxError} If the expression is invalid.
327
+ */
328
+ const parseCron = (expression) => {
329
+ const cached = _cronSpecs.get(expression);
330
+ if (cached) return cached;
331
+ const text = CRON_MACROS[expression.trim().toLowerCase()] ?? expression.trim();
332
+ const fields = text.split(/\s+/);
333
+ if (fields.length !== 5 && fields.length !== 6) throw new SyntaxError(`Invalid cron expression "${expression}": expected 5 or 6 fields`);
334
+ if (fields.length === 5) fields.unshift("0");
335
+ /** @type {(field: string, min: number, max: number, names?: string[]) => Set<number>} */
336
+ const parse = (field, min, max, names) => {
337
+ /** @type {Set<number>} */
338
+ const values = new Set();
339
+ for (const part of field.split(",")) {
340
+ const [rangePart, stepPart] = part.split("/");
341
+ const step = stepPart === undefined ? 1 : Number(stepPart);
342
+ if (!Number.isInteger(step) || step < 1) throw new SyntaxError(`Invalid step "${part}" in cron expression "${expression}"`);
343
+ const toNumber = (/** @type {string} */ token) => {
344
+ const index = names ? names.indexOf(token.toLowerCase()) : -1;
345
+ const n = index >= 0 ? index + (min === 1 ? 1 : 0) : token === "" ? NaN : Number(token);
346
+ if (!Number.isInteger(n)) throw new SyntaxError(`Invalid value "${token}" in cron expression "${expression}"`);
347
+ return n;
348
+ };
349
+ let from = min;
350
+ let to = max;
351
+ if (rangePart !== "*" && rangePart !== "?") {
352
+ const [a, b] = rangePart.split("-");
353
+ from = toNumber(a);
354
+ to = b === undefined ? (stepPart === undefined ? from : max) : toNumber(b);
355
+ }
356
+ if (from < min || to > max || from > to) throw new SyntaxError(`Value out of range in "${part}" (allowed ${min}-${max}) in cron expression "${expression}"`);
357
+ for (let v = from; v <= to; v += step) values.add(v);
358
+ }
359
+ return values;
360
+ };
361
+ const weekdays = parse(fields[5], 0, 7, DAY_NAMES);
362
+ if (weekdays.has(7)) {
363
+ weekdays.delete(7);
364
+ weekdays.add(0);
365
+ }
366
+ // "L" alone, or in a list like "15,L", means the last day of the month.
367
+ const dayParts = fields[3].split(",");
368
+ const lastDay = dayParts.some((part) => part.toUpperCase() === "L");
369
+ const dayField = dayParts.filter((part) => part.toUpperCase() !== "L").join(",");
370
+ /** @type {CronSpec} */
371
+ const spec = {
372
+ seconds: parse(fields[0], 0, 59),
373
+ minutes: parse(fields[1], 0, 59),
374
+ hours: parse(fields[2], 0, 23),
375
+ days: dayField === "" ? new Set() : parse(dayField, 1, 31),
376
+ months: parse(fields[4], 1, 12, MONTH_NAMES),
377
+ weekdays,
378
+ anyDay: fields[3] === "*" || fields[3] === "?",
379
+ anyWeekday: fields[5] === "*" || fields[5] === "?",
380
+ lastDay,
381
+ };
382
+ if (_cronSpecs.size >= 200) _cronSpecs.clear();
383
+ _cronSpecs.set(expression, spec);
384
+ return spec;
385
+ };
386
+
387
+ /**
388
+ * Blocks the thread for a few milliseconds. Only for short retries of
389
+ * synchronous file operations.
390
+ * @param {number} ms
391
+ */
392
+ const sleepSync = (ms) => {
393
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
394
+ };
395
+
396
+ module.exports = {
397
+ UNSAFE_KEYS,
398
+ MS,
399
+ timeConfig,
400
+ ASCII,
401
+ isPlainObject,
402
+ unitOf,
403
+ parseDuration,
404
+ toMs,
405
+ setLongTimeout,
406
+ sleep,
407
+ sleepSync,
408
+ intl,
409
+ segmenter,
410
+ graphemes,
411
+ graphemeCount,
412
+ preciseSum,
413
+ argsKey,
414
+ parseCron,
415
+ };
package/types/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Generated by scripts/build-types.js. Do not edit by hand.
2
- // @ix-xs/node-comfort v2.0.0
2
+ // @ix-xs/node-comfort v2.1.0
3
3
 
4
4
  import _Arr = require("./src/Arr");
5
5
  import _Async = require("./src/Async");
@@ -49,8 +49,9 @@ declare namespace nodeComfort {
49
49
  * `#ff8800`, `bg#1e1e2e`, `rgb(255, 128, 0)` and `bgRgb(...)`.
50
50
  *
51
51
  * Levels, from chatty to severe: `trace`, `debug`, `info`, `warn`, `error`,
52
- * `fatal`. The default is `info`, or `debug` when `DEBUG` is set; `LOG_LEVEL`
53
- * overrides it. Warnings and errors go to stderr.
52
+ * `fatal`. The default is `info`; `LOG_LEVEL` changes it. `DEBUG=1` shows
53
+ * debug messages, and `DEBUG=db,http:*` only the debug and trace messages of
54
+ * the loggers with these scopes. Warnings and errors go to stderr.
54
55
  *
55
56
  * @example
56
57
  * nc.info("Server listening on port 3000");
@@ -169,15 +170,16 @@ declare namespace nodeComfort {
169
170
  */
170
171
  export import time = _Time;
171
172
  /**
172
- * Unique ids: UUID v4 and v7, ULID, nanoid-style ids, Snowflakes, tokens
173
- * and short codes. All of them use the secure random generator of
174
- * `node:crypto`, with no bias.
173
+ * Unique ids: UUID v4, v5 and v7, ULID, ObjectId, nanoid-style ids,
174
+ * Snowflakes, tokens and short codes. Random ones use the secure generator
175
+ * of `node:crypto`, with no bias.
175
176
  *
176
177
  * @example
177
- * id.uuidv7(); // "0190a5c4-7c1e-7b2a-9f3e-1c2d3e4f5a6b", sorts by creation time
178
- * id.nano(); // "V1StGXR8_Z5jdHi6B-myT"
179
- * id.code(); // "K7QF9X"
180
- * id.token(32); // for API keys and reset links
178
+ * id.uuidv7(); // "0190a5c4-7c1e-7b2a-9f3e-1c2d3e4f5a6b", sorts by creation time
179
+ * id.nano(); // "V1StGXR8_Z5jdHi6B-myT"
180
+ * id.prefixed("user"); // "user_7Hq2mXc0LrT9aZ4bWk1PdE8s"
181
+ * id.code(); // "K7QF9X"
182
+ * id.token(32); // for API keys and reset links
181
183
  */
182
184
  export import id = _Id;
183
185
  /**
@@ -262,7 +264,7 @@ declare namespace nodeComfort {
262
264
  * });
263
265
  *
264
266
  * const user = User.parse(req.body); // typed, or a ValidationError listing every issue
265
- * const result = User.safeParse(input); // { success, data } or { success, error }
267
+ * const result = User.safeParse(input); // { success, data } or { success, error, issues }
266
268
  */
267
269
  export import schema = _Schema;
268
270
  /**
@@ -284,7 +286,8 @@ declare namespace nodeComfort {
284
286
  *
285
287
  * The `.env` file in the working directory is loaded when the package is
286
288
  * required; variables that are already set are never overwritten. Set
287
- * `NODE_COMFORT_DOTENV=false` to turn that off.
289
+ * `NODE_COMFORT_DOTENV=false` to turn that off. What it loaded has the
290
+ * lowest priority: a file you load yourself afterwards wins over it.
288
291
  *
289
292
  * @example
290
293
  * const config = env.validate({
@@ -296,9 +299,13 @@ declare namespace nodeComfort {
296
299
  */
297
300
  export import env = _Env;
298
301
  /**
299
- * The errors node-comfort throws. They all extend `NodeComfortError` (itself
300
- * a regular `Error`) and carry a stable `code`, so you can branch on the
301
- * kind of failure without parsing messages.
302
+ * The errors node-comfort throws when something goes wrong at run time: a
303
+ * request fails, data is invalid, a command exits badly. They all extend
304
+ * `NodeComfortError` (itself a regular `Error`) and carry a stable `code`,
305
+ * so you can branch on the kind of failure without parsing messages.
306
+ *
307
+ * A mistake in your own code, like a wrong argument, still throws a plain
308
+ * `TypeError` or `RangeError`, as it does everywhere in JavaScript.
302
309
  *
303
310
  * @example
304
311
  * try {
@@ -387,6 +394,7 @@ declare namespace nodeComfort {
387
394
  export import getFilesIn = _FS.getFilesIn;
388
395
  export import glob = _FS.glob;
389
396
  export import find = _FS.find;
397
+ export import findUp = _FS.findUp;
390
398
  export import readFile = _FS.readFile;
391
399
  export import readLines = _FS.readLines;
392
400
  export import readJSON = _FS.readJSON;
@@ -484,6 +492,18 @@ declare namespace nodeComfort {
484
492
  export import isAlphanumeric = _Checker.isAlphanumeric;
485
493
  export import isCreditCard = _Checker.isCreditCard;
486
494
  export import isJWT = _Checker.isJWT;
495
+ export import isHostname = _Checker.isHostname;
496
+ export import isMACAddress = _Checker.isMACAddress;
497
+ export import isIBAN = _Checker.isIBAN;
498
+ export import isE164 = _Checker.isE164;
499
+ export import isObjectId = _Checker.isObjectId;
500
+ export import isULID = _Checker.isULID;
501
+ export import isDataURL = _Checker.isDataURL;
502
+ export import isMimeType = _Checker.isMimeType;
503
+ export import isTimeZone = _Checker.isTimeZone;
504
+ export import isLocale = _Checker.isLocale;
505
+ export import isCron = _Checker.isCron;
506
+ export import isASCII = _Checker.isASCII;
487
507
  export import assert = _Checker.assert;
488
508
  export import assertType = _Checker.assertType;
489
509
  export import wait = _Utils.wait;
@@ -495,8 +515,11 @@ declare namespace nodeComfort {
495
515
  // Types
496
516
  export import AnyTypedArray = _Checker.AnyTypedArray;
497
517
  export import ArgsOptions = _Cli.ArgsOptions;
518
+ export import BatchedFunction = _Func.BatchedFunction;
519
+ export import BatchOptions = _Func.BatchOptions;
498
520
  export import BorderStyle = _Cli.BorderStyle;
499
521
  export import BoxOptions = _Cli.BoxOptions;
522
+ export import CacheEntry = _Cache.CacheEntry;
500
523
  export import CacheOptions = _Cache.CacheOptions;
501
524
  export import CacheRemovalReason = _Cache.CacheRemovalReason;
502
525
  export import CacheSetOptions = _Cache.CacheSetOptions;
@@ -527,9 +550,9 @@ declare namespace nodeComfort {
527
550
  export import CopyFoldersOptions = _FS.CopyFoldersOptions;
528
551
  export import CopyOptions = _FS.CopyOptions;
529
552
  export import CronOptions = _Time.CronOptions;
530
- export import CronSpec = _Time.CronSpec;
531
553
  export import CurrencyOptions = _Num.CurrencyOptions;
532
554
  export import DateInput = _Time.DateInput;
555
+ export import DateRangeOptions = _Time.DateRangeOptions;
533
556
  export import DebouncedFunction = _Func.DebouncedFunction;
534
557
  export import DebounceOptions = _Func.DebounceOptions;
535
558
  export import DecodedJWT = _Crypto.DecodedJWT;
@@ -562,24 +585,32 @@ declare namespace nodeComfort {
562
585
  export import HashPasswordOptions = _Crypto.HashPasswordOptions;
563
586
  export import HttpBody = _Http.HttpBody;
564
587
  export import HttpClient = _Http.HttpClient;
588
+ export import HttpHeaders = _Http.HttpHeaders;
565
589
  export import HttpHooks = _Http.HttpHooks;
566
590
  export import HttpMethod = _Http.HttpMethod;
567
591
  export import HttpOptions = _Http.HttpOptions;
592
+ export import HttpOptionsOf = _Http.HttpOptionsOf;
568
593
  export import HttpRequestConfig = _Http.HttpRequestConfig;
569
594
  export import HttpResponse = _Http.HttpResponse;
570
595
  export import HttpRetryOptions = _Http.HttpRetryOptions;
571
596
  export import IndexDefinition = _SQLite.IndexDefinition;
572
597
  export import Infer = _Schema.Infer;
598
+ export import InsertOptions = _SQLite.InsertOptions;
573
599
  export import InsertResult = _SQLite.InsertResult;
574
600
  export import IsBase64Options = _Checker.IsBase64Options;
601
+ export import IsHostnameOptions = _Checker.IsHostnameOptions;
575
602
  export import IsURLOptions = _Checker.IsURLOptions;
576
603
  export import IsUUIDOptions = _Checker.IsUUIDOptions;
577
604
  export import Iteratee = _Arr.Iteratee;
605
+ export import JSONSchema = _Schema.JSONSchema;
578
606
  export import JWTAlgorithm = _Crypto.JWTAlgorithm;
579
607
  export import JWTClaims = _Crypto.JWTClaims;
608
+ export import JWTKey = _Crypto.JWTKey;
609
+ export import KeyPair = _Crypto.KeyPair;
580
610
  export import Limiter = _Async.Limiter;
581
- export import LineReader = _Cli.LineReader;
611
+ export import ListenOptions = _Emitter.ListenOptions;
582
612
  export import ListOptions = _FS.ListOptions;
613
+ export import LogEntry = _Logger.LogEntry;
583
614
  export import LogFileOptions = _Logger.LogFileOptions;
584
615
  export import Logger = _Logger.Logger;
585
616
  export import LoggerOptions = _Logger.LoggerOptions;
@@ -598,9 +629,11 @@ declare namespace nodeComfort {
598
629
  export import ObjectOutput = _Schema.ObjectOutput;
599
630
  export import OrdinalOptions = _Num.OrdinalOptions;
600
631
  export import Page = _Arr.Page;
632
+ export import PaginateOptions = _SQLite.PaginateOptions;
601
633
  export import ParseBytesOptions = _Num.ParseBytesOptions;
602
634
  export import ParseContext = _Schema.ParseContext;
603
635
  export import ParsedArgs = _Cli.ParsedArgs;
636
+ export import ParseDateOptions = _Time.ParseDateOptions;
604
637
  export import ParsedFlags = _Cli.ParsedFlags;
605
638
  export import ParsedSnowflake = _Id.ParsedSnowflake;
606
639
  export import PathInput = _Obj.PathInput;
@@ -621,9 +654,11 @@ declare namespace nodeComfort {
621
654
  export import QueueAddOptions = _Async.QueueAddOptions;
622
655
  export import QueueOptions = _Async.QueueOptions;
623
656
  export import RandomStringOptions = _Str.RandomStringOptions;
657
+ export import RateLimiter = _Async.RateLimiter;
624
658
  export import ReadJSONOptions = _FS.ReadJSONOptions;
625
659
  export import RefineOptions = _Schema.RefineOptions;
626
660
  export import RelativeOptions = _Time.RelativeOptions;
661
+ export import ResponseSchema = _Http.ResponseSchema;
627
662
  export import RetryOptions = _Func.RetryOptions;
628
663
  export import RunOptions = _Sys.RunOptions;
629
664
  export import RunResult = _Sys.RunResult;
@@ -633,6 +668,7 @@ declare namespace nodeComfort {
633
668
  export import Settled = _Async.Settled;
634
669
  export import Shape = _Schema.Shape;
635
670
  export import ShutdownOptions = _Sys.ShutdownOptions;
671
+ export import ShutdownReason = _Sys.ShutdownReason;
636
672
  export import SignJWTOptions = _Crypto.SignJWTOptions;
637
673
  export import Simplify = _Schema.Simplify;
638
674
  export import SlugifyOptions = _Str.SlugifyOptions;
@@ -640,8 +676,10 @@ declare namespace nodeComfort {
640
676
  export import SortByOptions = _Arr.SortByOptions;
641
677
  export import SortDirection = _SQLite.SortDirection;
642
678
  export import SortKey = _Arr.SortKey;
679
+ export import SortKeysOptions = _Obj.SortKeysOptions;
643
680
  export import Spinner = _Cli.Spinner;
644
681
  export import SpinnerOptions = _Cli.SpinnerOptions;
682
+ export import SpinOptions = _Cli.SpinOptions;
645
683
  export import SQLiteOptions = _SQLite.SQLiteOptions;
646
684
  export import StartOfUnit = _Time.StartOfUnit;
647
685
  export import Step = _Schema.Step;
@@ -659,9 +697,13 @@ declare namespace nodeComfort {
659
697
  export import ThrottleOptions = _Func.ThrottleOptions;
660
698
  export import TimeZoneOptions = _Time.TimeZoneOptions;
661
699
  export import TokenEncoding = _Id.TokenEncoding;
700
+ export import ToSentenceOptions = _Arr.ToSentenceOptions;
662
701
  export import TOTPOptions = _Crypto.TOTPOptions;
663
702
  export import TOTPURIOptions = _Crypto.TOTPURIOptions;
703
+ export import TransactionOptions = _SQLite.TransactionOptions;
704
+ export import TreeOptions = _Cli.TreeOptions;
664
705
  export import TruncateOptions = _Str.TruncateOptions;
706
+ export import UUIDNamespace = _Id.UUIDNamespace;
665
707
  export import ValidationIssue = _errors.ValidationIssue;
666
708
  export import VarianceOptions = _Num.VarianceOptions;
667
709
  export import VerifyJWTOptions = _Crypto.VerifyJWTOptions;