@ix-xs/node-comfort 1.2.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.
- package/CHANGELOG.md +340 -0
- package/README.md +137 -548
- package/index.js +80 -35
- package/package.json +161 -33
- package/src/Arr.js +1050 -418
- package/src/Async.js +817 -0
- package/src/Cache.js +433 -0
- package/src/Checker.js +1202 -539
- package/src/Cli.js +1293 -0
- package/src/Color.js +556 -0
- package/src/Crypto.js +1082 -0
- package/src/Emitter.js +356 -0
- package/src/Env.js +689 -0
- package/src/FS.js +1626 -894
- package/src/Func.js +823 -356
- package/src/Http.js +700 -0
- package/src/Id.js +568 -200
- package/src/Logger.js +963 -378
- package/src/Num.js +965 -360
- package/src/Obj.js +1077 -379
- package/src/SQLite.js +1629 -1005
- package/src/Schema.js +2101 -0
- package/src/Str.js +1209 -432
- package/src/Sys.js +654 -0
- package/src/Time.js +1533 -440
- package/src/Utils.js +342 -325
- package/src/errors.js +335 -0
- package/src/internal/shared.js +415 -0
- package/types/index.d.ts +735 -526
- package/types/src/Arr.d.ts +533 -145
- package/types/src/Arr.d.ts.map +1 -1
- package/types/src/Async.d.ts +372 -0
- package/types/src/Async.d.ts.map +1 -0
- package/types/src/Cache.d.ts +221 -0
- package/types/src/Cache.d.ts.map +1 -0
- package/types/src/Checker.d.ts +651 -232
- package/types/src/Checker.d.ts.map +1 -1
- package/types/src/Cli.d.ts +664 -0
- package/types/src/Cli.d.ts.map +1 -0
- package/types/src/Color.d.ts +300 -0
- package/types/src/Color.d.ts.map +1 -0
- package/types/src/Crypto.d.ts +620 -0
- package/types/src/Crypto.d.ts.map +1 -0
- package/types/src/Emitter.d.ts +189 -0
- package/types/src/Emitter.d.ts.map +1 -0
- package/types/src/Env.d.ts +412 -0
- package/types/src/Env.d.ts.map +1 -0
- package/types/src/FS.d.ts +717 -238
- package/types/src/FS.d.ts.map +1 -1
- package/types/src/Func.d.ts +508 -135
- package/types/src/Func.d.ts.map +1 -1
- package/types/src/Http.d.ts +444 -0
- package/types/src/Http.d.ts.map +1 -0
- package/types/src/Id.d.ts +199 -66
- package/types/src/Id.d.ts.map +1 -1
- package/types/src/Logger.d.ts +414 -46
- package/types/src/Logger.d.ts.map +1 -1
- package/types/src/Num.d.ts +514 -137
- package/types/src/Num.d.ts.map +1 -1
- package/types/src/Obj.d.ts +453 -104
- package/types/src/Obj.d.ts.map +1 -1
- package/types/src/SQLite.d.ts +754 -308
- package/types/src/SQLite.d.ts.map +1 -1
- package/types/src/Schema.d.ts +1189 -0
- package/types/src/Schema.d.ts.map +1 -0
- package/types/src/Str.d.ts +617 -166
- package/types/src/Str.d.ts.map +1 -1
- package/types/src/Sys.d.ts +314 -0
- package/types/src/Sys.d.ts.map +1 -0
- package/types/src/Time.d.ts +759 -204
- package/types/src/Time.d.ts.map +1 -1
- package/types/src/Utils.d.ts +158 -102
- package/types/src/Utils.d.ts.map +1 -1
- package/types/src/errors.d.ts +316 -0
- package/types/src/errors.d.ts.map +1 -0
- package/types/src/internal/shared.d.ts +123 -0
- package/types/src/internal/shared.d.ts.map +1 -0
- package/types/index.d.ts.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project uses
|
|
5
|
+
[Semantic Versioning](https://semver.org/).
|
|
6
|
+
|
|
7
|
+
## [2.1.0] - 2026-10-12
|
|
8
|
+
|
|
9
|
+
Every module was read again, line by line. The result: about sixty new functions and options, a hot path
|
|
10
|
+
that is several times faster almost everywhere, a long list of edge cases fixed, and a new documentation
|
|
11
|
+
website. Nothing has to change in a project that used 2.0 the documented way. The few behaviours that
|
|
12
|
+
did change are listed under "Changed".
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **logger**: `redact` hides the keys you name in everything that is logged, `onLog` receives each entry
|
|
17
|
+
(`LogEntry`) to forward it to another service, and `DEBUG=db,http:*` chooses which scopes print their
|
|
18
|
+
debug lines.
|
|
19
|
+
- **fs**: `fs.findUp()` (also `nc.findUp()`) walks up the folders to find a file, `writeFile()` and
|
|
20
|
+
`writeJSON()` take `fsync` to wait for the disk, and watchers emit `"error"`.
|
|
21
|
+
- **checker**: `isHostname`, `isMACAddress`, `isIBAN`, `isE164`, `isObjectId`, `isULID`, `isDataURL`,
|
|
22
|
+
`isMimeType`, `isTimeZone`, `isLocale`, `isCron` and `isASCII`.
|
|
23
|
+
- **env**: `env.example()` writes a `.env.example` from your specification, and `load({ cascade })` reads
|
|
24
|
+
`.env`, `.env.local`, `.env.{mode}` and `.env.{mode}.local` in the usual order.
|
|
25
|
+
- **schema**: `pipe()`, `discriminatedUnion()`, `toJSONSchema()`, object `required()`, `merge()` and
|
|
26
|
+
`keyof()`, number `gt()`, `lt()` and `safe()`, string `date()`, `slug()`, `hex()`, `base64()`, `ulid()`,
|
|
27
|
+
`semver()` and `hostname()`. `safeParse()` gives the list of `issues` without building an error.
|
|
28
|
+
- **http**: `params` fills a path such as `"users/:id"`, `schema` validates the response and gives
|
|
29
|
+
`data` its type,
|
|
30
|
+
`cookies` lists every `Set-Cookie`, headers can be a `Headers` object or pairs, and retries take `jitter`
|
|
31
|
+
and text delays (`"2s"`).
|
|
32
|
+
- **SQLite**: filters and sorts on JSON paths (`"settings.theme"`), `paginate()`, `increment()`,
|
|
33
|
+
`onConflict` for `insert()` and `insertMany()`, `defaultExpression` for columns,
|
|
34
|
+
`transaction({ immediate })`, a `synchronous` option, and reads from views and temporary tables.
|
|
35
|
+
- **time**: `time.parse()` reads a date from text with a pattern, `isSame()`, `range()`, `fromUnix()`, the
|
|
36
|
+
`Do` and `z` tokens, and `formatDuration()` accepts text such as `"90m"`.
|
|
37
|
+
- **id**: `uuidv5()`, `objectId()` and `prefixed()`.
|
|
38
|
+
- **crypto**: `verifyHmac()` for webhook signatures, `generateKeyPair()`, and JSON Web Tokens signed with
|
|
39
|
+
RSA, ECDSA or Ed25519 keys (`RS256`, `PS256`, `ES256`, `EdDSA`...).
|
|
40
|
+
- **cli**: `cli.tree()`, `cli.spin()` runs a promise under a spinner, `args()` learns commands
|
|
41
|
+
(`commands`), `env` fallbacks, `examples` in the help and `choices` for numbers. Tables accept cells of
|
|
42
|
+
several lines. Menus understand Home, End, Page Up, Page Down and Escape.
|
|
43
|
+
- **color**: `color.truncate()` cuts colored text to a width without breaking its colors.
|
|
44
|
+
- **sys**: `shutdown()`, `freePort()` and `localIP()`.
|
|
45
|
+
- **func**: `batch()` groups calls into one, `dedupe()` shares a call that is already running, and
|
|
46
|
+
`pipeAsync()`.
|
|
47
|
+
- **async**: `parallel()`, `rateLimit()` and `abortable()`.
|
|
48
|
+
- **Cache**: `getOrSet(..., { staleWhileRevalidate })` answers with the old value while the new one loads,
|
|
49
|
+
and `resetStats()`.
|
|
50
|
+
- **Emitter**: `on()` and `once()` take `{ signal }`, and `listeners()`.
|
|
51
|
+
- **str**: `upperFirst`, `lowerFirst`, `humanize` and `chunk`. **num**: `pad`, `percentChange` and
|
|
52
|
+
`product`. **arr**: `duplicates`, `xor`, `takeWhile`, `dropWhile` and `toSentence`. **obj**: `update`,
|
|
53
|
+
`sortKeys` and `paths`.
|
|
54
|
+
- **Durations are text everywhere.** The last options that only took milliseconds (`debounce`, `throttle`,
|
|
55
|
+
`memoize`, `retry`, `timeout`, `delay`, `waitFor`...) accept `"5m"` or `"1.5s"` like the rest of the
|
|
56
|
+
package.
|
|
57
|
+
- Errors serialize with `JSON.stringify()` (name, code, message, fields and cause), and `instanceof` works
|
|
58
|
+
across two copies of the package.
|
|
59
|
+
|
|
60
|
+
### Faster
|
|
61
|
+
|
|
62
|
+
Measured on the same machine, before and after, with Node 24.
|
|
63
|
+
|
|
64
|
+
| Operation | Speedup |
|
|
65
|
+
| --- | --- |
|
|
66
|
+
| `nc.info()` | 124x |
|
|
67
|
+
| `time.parseDuration("5m")` | 68x |
|
|
68
|
+
| `str.plural()` | 57x |
|
|
69
|
+
| `time.format()` with month and day names | 39x |
|
|
70
|
+
| `id.code()` | 39x |
|
|
71
|
+
| `id.uuidv7()` | 28x |
|
|
72
|
+
| `num.format()` | 23x |
|
|
73
|
+
| `checker.isIP()` | 19x |
|
|
74
|
+
| `obj.get()` with a dot path | 14x |
|
|
75
|
+
| `emitter.emit()` | 10x |
|
|
76
|
+
| `schema.is()` on invalid data | 9x |
|
|
77
|
+
| `schema.safeParse()` on invalid data | 5x |
|
|
78
|
+
| `db.get()` and `db.getAll()` | 3x to 4x |
|
|
79
|
+
|
|
80
|
+
`sys.which("node")` went from 23 ms to 0.13 ms on Windows. What is slow in JavaScript (`Intl` objects,
|
|
81
|
+
random bytes, stack traces, prepared statements) is now created once and reused.
|
|
82
|
+
|
|
83
|
+
### Changed
|
|
84
|
+
|
|
85
|
+
Behaviours that differ from 2.0. Each one fixes something that was wrong or unsafe.
|
|
86
|
+
|
|
87
|
+
- **logger**: `DEBUG=name` only turns on the debug lines of the scopes it names (it used to turn on all
|
|
88
|
+
of them). `DEBUG=*`, `DEBUG=1` and `LOG_LEVEL=debug` show everything.
|
|
89
|
+
- **time**: `startOf()`, `endOf()`, `add()` and `diff()` follow `setTimezone()` instead of the system zone.
|
|
90
|
+
Adding whole days or weeks keeps the time of day when the clocks change in between (it added 24 hours
|
|
91
|
+
per day); `"24h"` stays exact. `add(date, "soon")` throws a `TypeError` (unreadable text used to add
|
|
92
|
+
nothing), and so does a unit that does not exist (it was read as milliseconds).
|
|
93
|
+
- **crypto**: `decrypt()` always fails with a `NodeComfortError` whose code is `ERR_DECRYPT` (malformed
|
|
94
|
+
input used to throw a `TypeError`), and refuses shortened authentication tags. `hash()` and `hmac()`
|
|
95
|
+
throw a `TypeError` for `null`, `undefined`, plain objects and empty secrets. `safeEqual()` returns
|
|
96
|
+
`false` when a value is missing. A wrong key type for a token is a `TypeError`, and the algorithms a
|
|
97
|
+
token may use follow from the key that verifies it.
|
|
98
|
+
- **cli**: leaving a menu or a password prompt with Ctrl+C or Escape rejects with an `AbortError`.
|
|
99
|
+
`cli.table([])` returns `""`. The
|
|
100
|
+
`LineReader` type is gone from the public types.
|
|
101
|
+
- **http**: the query and headers of a call are added to those of the client instead of replacing them.
|
|
102
|
+
A request whose body is a stream is never retried. With `responseType: "stream"` the timeout stops when
|
|
103
|
+
the headers arrive. `download()` writes to a temporary file first, so a failed download never destroys
|
|
104
|
+
the file that was there.
|
|
105
|
+
- **schema**: `pick()`, `omit()`, `partial()` and `extend()` keep `strict()`. Names inherited from
|
|
106
|
+
`Object.prototype` (`toString`, `constructor`) are never read from the input.
|
|
107
|
+
- **SQLite**: a column whose default is `CURRENT_TIMESTAMP` gets the time of the insert (it used to store
|
|
108
|
+
that text), and with `dates: true` the dates SQLite writes itself are read as UTC. Columns of joins and renamed columns are converted like the others, with Node 22.16 or later.
|
|
109
|
+
- **env**: in `load()`, the first file that defines a variable wins, whatever `override` says. `override`
|
|
110
|
+
only decides whether files may replace what was already in the environment. A file you load yourself
|
|
111
|
+
wins over the `.env` loaded at startup, so `.env.local` and `cascade` work without turning anything off.
|
|
112
|
+
A reference such as `${HOST}` takes the value `HOST` really ends up with.
|
|
113
|
+
- **sys**: `run()` rejects on a timeout it cannot read (it used to mean no timeout), and on Windows a
|
|
114
|
+
timeout or an abort stops the whole process tree.
|
|
115
|
+
- **fs**: an atomic write keeps the permissions of the file it replaces.
|
|
116
|
+
- **func**: `retry()` stops as soon as the function is aborted. `memoize()` no longer confuses
|
|
117
|
+
`f(undefined)` with `f(null)`, and accepts `bigint` arguments.
|
|
118
|
+
- **num**: `min()` and `max()` return `NaN` when one value is `NaN`, like `Math.min()`. `sum([0.1, 0.2, 0.3])`
|
|
119
|
+
is `0.6`.
|
|
120
|
+
- **obj**: `isEmpty()` looks inside a `Map` and a `Set`, and is `false` for a `Date`, a `RegExp` and an
|
|
121
|
+
`Error`. `unset()` on an array index removes the item instead of leaving a hole. `clone()` keeps class
|
|
122
|
+
instances and copies buffers. `merge()` never shares a nested object with its inputs.
|
|
123
|
+
- **checker**: `isEmpty()` is `false` for a `Date`, a `RegExp` and an `Error`.
|
|
124
|
+
- **utils**: `JSONString()` never throws, and writes an error with its name, message and stack.
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
|
|
128
|
+
- Timers longer than 24.8 days (`wait("30d")`, cache expiry, `timeout()`) no longer fire at once.
|
|
129
|
+
- Object helpers (`pick`, `omit`, `filter`, `mapValues`, `flatten`, `groupBy`, `keyBy`, `countBy`, `props`,
|
|
130
|
+
`env.parse`...) are safe with keys named `__proto__`, `constructor` or `toString`.
|
|
131
|
+
- `safeEqual()` no longer reveals the length of the secret.
|
|
132
|
+
- `str.stripTags()` leaves `a < b and c > d` alone, `unescapeHTML()` accepts invalid code points,
|
|
133
|
+
`capitalize()` handles letters outside the basic plane, and padding counts what you see.
|
|
134
|
+
- `num.formatBytes()` and `num.abbreviate()` change unit when rounding reaches the next one (`"1 MB"`,
|
|
135
|
+
not `"1024 KB"`).
|
|
136
|
+
- `isYesterday()` and `isTomorrow()` are right on the days the clocks change.
|
|
137
|
+
- `cache.getOrSet()` respects a `delete()`, a `clear()` or a `set()` made while the value was loading.
|
|
138
|
+
- A shutdown handler that never finishes can no longer let the process end with a success code.
|
|
139
|
+
- `sys.run()` and `sys.exec()` settle after a timeout or an abort even when something the command started
|
|
140
|
+
keeps its output open. They used to wait for it.
|
|
141
|
+
- An async `onLog` hook that fails no longer stops the process.
|
|
142
|
+
- Deleting a watched folder on Windows sent events without end. The watcher now reports it once and stops.
|
|
143
|
+
- `http.download()` reports a connection cut in the middle as an `HttpError` with the code `ERR_NETWORK`.
|
|
144
|
+
- `str.truncate()` never returns more than the length asked for, even when the omission is longer.
|
|
145
|
+
- `emitter.off()` removes a listener added with `once()`.
|
|
146
|
+
- `http` keeps every `Set-Cookie` header and reads the status of an error response in all cases.
|
|
147
|
+
- Tables no longer fail on very large inputs, and progress bars print labels that contain `$`.
|
|
148
|
+
|
|
149
|
+
### Documentation
|
|
150
|
+
|
|
151
|
+
- A new website, in English and in French, with a search, a page for every namespace generated from the
|
|
152
|
+
types, and a landing page whose examples are real files, run at every build.
|
|
153
|
+
- The old addresses (`/guide/logger.html`, `/api/str.html`) redirect to the new pages.
|
|
154
|
+
- `docs/API.md` is gone: the reference lives on the website, and in your editor.
|
|
155
|
+
|
|
156
|
+
## [2.0.0] - 2026-09-12
|
|
157
|
+
|
|
158
|
+
node-comfort becomes a complete, zero-dependency standard library for Node.js: 23 namespaces and classes,
|
|
159
|
+
more than 500 documented and typed functions, and an editor experience checked on every commit.
|
|
160
|
+
|
|
161
|
+
### Security
|
|
162
|
+
|
|
163
|
+
- **Prototype pollution fixed** in `obj.set()` and `obj.unflatten()` (1.2.0). A path such as
|
|
164
|
+
`"__proto__.isAdmin"` coming from user input could modify `Object.prototype`. Every object helper
|
|
165
|
+
(`set`, `unset`, `merge`, `mergeWith`, `defaults`, `unflatten`, `clone`, `fromEntries`...) now ignores
|
|
166
|
+
`__proto__`, `constructor` and `prototype` keys. **Upgrade if you pass user-controlled paths or objects
|
|
167
|
+
to these functions.**
|
|
168
|
+
- The 1.x filesystem helpers searched the whole folder tree for a file with the same name when a relative
|
|
169
|
+
path did not exist, which could read, overwrite or delete the wrong file. This implicit search is gone
|
|
170
|
+
(see `fs` below).
|
|
171
|
+
- `id.code()` no longer has a modulo bias (rejection sampling).
|
|
172
|
+
|
|
173
|
+
### Breaking changes
|
|
174
|
+
|
|
175
|
+
Each item explains what to change. Most projects only need the SQLite and Node.js items.
|
|
176
|
+
|
|
177
|
+
1. **Node.js 22.13 or newer is required** (`engines`). Node 20 reaches end of life, and 1.x already
|
|
178
|
+
required `node:sqlite`, which is only available without a flag since Node 22.13.
|
|
179
|
+
2. **Deep imports are no longer allowed**: the package defines an `exports` map. Replace
|
|
180
|
+
`require("@ix-xs/node-comfort/src/Logger")` by `require("@ix-xs/node-comfort").logger`, or by the
|
|
181
|
+
official entry point of the namespace: `require("@ix-xs/node-comfort/logger")`.
|
|
182
|
+
3. **SQLite methods throw instead of returning `{ error }`.** Failures throw a `nc.errors.SQLiteError`
|
|
183
|
+
(with `sql` and `sqliteCode`, for example `"SQLITE_CONSTRAINT_UNIQUE"`). Success values are unchanged.
|
|
184
|
+
```js
|
|
185
|
+
// 1.x
|
|
186
|
+
const result = await db.insert("users", data);
|
|
187
|
+
if (result.error) console.error(result.error);
|
|
188
|
+
// 2.0
|
|
189
|
+
try {
|
|
190
|
+
db.insert("users", data);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
if (error.sqliteCode === "SQLITE_CONSTRAINT_UNIQUE") console.error("Email already used");
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
`db.transaction()` re-throws the error after rolling back (it used to return `{ error }`).
|
|
196
|
+
4. **SQLite methods are synchronous.** They return values directly, like the underlying engine. `await`
|
|
197
|
+
keeps working, but `.then()` chains on their results must be removed. `db.transaction()` still accepts
|
|
198
|
+
async callbacks (it then returns a promise).
|
|
199
|
+
5. **`db.getAll()` no longer limits to 50 rows and no longer sorts by default.** Pass `{ limit: 50 }` to keep
|
|
200
|
+
a limit, and `{ orderBy: "id", direction: "DESC" }` to keep the old order. When `orderBy` is given without
|
|
201
|
+
`direction`, the order is now ascending (it was descending).
|
|
202
|
+
6. **SQLite tables are read from the database file.** 1.x only knew the tables created with `createTable()`
|
|
203
|
+
in the same process (`get()` returned `undefined` and `getAll()` returned `[]` for the others). Tables are
|
|
204
|
+
now found through `sqlite_master`, and an unknown table throws a `SQLiteError`.
|
|
205
|
+
7. **SQLite `BOOLEAN` and `JSON` columns are converted.** Reading a `BOOLEAN` column returns `true`/`false`
|
|
206
|
+
(it returned `1`/`0`) and a `JSON` column returns the parsed value. Writing booleans, objects, arrays and
|
|
207
|
+
dates now works (1.x threw "cannot be bound"). Disable with `new nc.SQLite(path, { booleans: false, json: false })`.
|
|
208
|
+
8. **`warn`, `error` and `fatal` logs are written to stderr**, like `console.warn` and `console.error`.
|
|
209
|
+
Colors are disabled when the output is not a terminal (files, pipes, CI logs) and with `NO_COLOR`;
|
|
210
|
+
use `FORCE_COLOR=1` to force them.
|
|
211
|
+
9. **Logger markup: `overline` draws a real overline.** It used to strike the text through; use
|
|
212
|
+
`strikethrough` for that.
|
|
213
|
+
10. **`func.timeout()` rejects with a `nc.errors.TimeoutError`.** It is still an `Error` with the same
|
|
214
|
+
message; only `error.name` (`"TimeoutError"`) and `error.code` (`"ETIMEDOUT"`) are new.
|
|
215
|
+
11. **`num.round()` rounds negative halves away from zero** (`-2.5` gives `-3`, it gave `-2`), consistently
|
|
216
|
+
with positive numbers, and never returns `-0`. Decimal precision is fixed: `round(1.005, 2)` is `1.01`.
|
|
217
|
+
12. **`str.length()`, `str.reverse()`, `str.truncate()` and `str.center()` count graphemes**, not UTF-16
|
|
218
|
+
code units: `str.length("👨👩👧")` is `1`, and emoji or accents are never cut in half.
|
|
219
|
+
13. **The filesystem helpers no longer search the folder tree** when a relative path does not exist
|
|
220
|
+
(see Security). Use the new `nc.find("config.json")` to search explicitly.
|
|
221
|
+
14. **`dontCrash()` only removes the listeners it added.** 1.x removed every `uncaughtException`, `exit`
|
|
222
|
+
and signal listener of the process, including those of other libraries. Calling it again replaces the
|
|
223
|
+
previous handlers, and the returned controller has `dispose()`.
|
|
224
|
+
15. **Stricter or more complete validators**: `isEmail()` accepts international addresses but rejects
|
|
225
|
+
consecutive or trailing dots; `isUUID()` accepts versions 1 to 8 and the nil/max UUIDs (use
|
|
226
|
+
`{ version }` to restrict); `isURL()` accepts an options object (`protocols`, `requireTld`).
|
|
227
|
+
16. **`JSONString()` converts `Map` to objects and `Set` to arrays** (they gave `{}`), and an object used
|
|
228
|
+
twice is serialized twice instead of being replaced by `"[Circular]"` (only real cycles are).
|
|
229
|
+
|
|
230
|
+
### Deprecated
|
|
231
|
+
|
|
232
|
+
- `id.hash()`, `id.hmac()` and `id.safeEqual()` moved to `nc.crypto` (same signatures). The aliases keep
|
|
233
|
+
working and will be removed in 3.0.0.
|
|
234
|
+
|
|
235
|
+
### Added
|
|
236
|
+
|
|
237
|
+
**Packaging and editor experience**
|
|
238
|
+
- Lazy loading: `require("@ix-xs/node-comfort")` takes about 3 ms, and each namespace is loaded the first
|
|
239
|
+
time it is used. A script that only logs never loads the database, the HTTP client or the crypto code.
|
|
240
|
+
- An entry point per namespace (`@ix-xs/node-comfort/str`, `/http`, `/sqlite`...), which keeps ES module
|
|
241
|
+
imports light and makes bundling easier.
|
|
242
|
+
- A documentation website at https://ix-xs.github.io/node-comfort/: guides for every namespace, the full
|
|
243
|
+
API reference, recipes, and search.
|
|
244
|
+
- Complete TypeScript declarations generated from the JSDoc: hover documentation with examples for every
|
|
245
|
+
function, parameter and option, completion after `nc.` and `nodeComfort.`, auto-import of `nc`,
|
|
246
|
+
`nodeComfort` and every flat helper, type guards, typed dot paths, schema inference, generic caches,
|
|
247
|
+
typed events, SQLite columns suggested in `where` filters.
|
|
248
|
+
- ES module named imports (`import { str, isEmail, nc } from "@ix-xs/node-comfort"`) and
|
|
249
|
+
`const { nc } = require(...)`.
|
|
250
|
+
- `docs/API.md`, a complete API reference generated from the types.
|
|
251
|
+
- Tests (`node --test`) and CI on Linux, macOS and Windows with Node 22, 24 and the latest release;
|
|
252
|
+
editor checks with TypeScript 5.9, 6 and 7.
|
|
253
|
+
|
|
254
|
+
**New namespaces and classes**
|
|
255
|
+
- `nc.crypto`: `hashPassword`/`verifyPassword`/`needsRehash` (scrypt), `encrypt`/`decrypt` (AES-256-GCM),
|
|
256
|
+
`signJWT`/`verifyJWT`/`decodeJWT` (HS256/384/512), `totp`/`verifyTOTP`/`totpSecret`/`totpURI` (2FA),
|
|
257
|
+
`hash`, `hmac`, `safeEqual`, `generateKey`, `randomBytes`, `randomInt`, `toBase64`, `fromBase64`.
|
|
258
|
+
- `nc.color`: chainable terminal colors (`color.red.bold("x")`), hex/RGB/256 colors, automatic level
|
|
259
|
+
detection and downgrade, `strip`, `width`, `link`, `gradient`, `create`.
|
|
260
|
+
- `nc.cli`: typed `args` with generated `--help`, `prompt`, `password`, `confirm`, `select`,
|
|
261
|
+
`multiselect`, `spinner`, `progress`, `table`, `box`, `isInteractive`, `size`, `clear`.
|
|
262
|
+
- `nc.sys`: `run` (no shell, Windows-safe arguments), `exec`, `which`, `open`, `onShutdown` (graceful
|
|
263
|
+
shutdown), `isWindows`, `isMac`, `isLinux`, `isCI`, `isDocker`, `isWSL`, `info`, `memory`.
|
|
264
|
+
- `nc.async`: `map`/`forEach`/`filter` with a concurrency limit, `series`, `limit`, `queue` (priorities,
|
|
265
|
+
timeouts), `mutex`, `deferred`, `settle`, `props`, `poll`, `sleep`.
|
|
266
|
+
- `nc.schema`: schema validation with TypeScript inference (`string`, `number`, `boolean`, `bigint`, `date`,
|
|
267
|
+
`literal`, `enum`, `array`, `object`, `union`, `tuple`, `record`, `lazy`, `instanceOf`, `custom`, `coerce`,
|
|
268
|
+
`refine`, `transform`, `default`, `catch`...).
|
|
269
|
+
- `nc.http`: HTTP client on top of `fetch` with JSON, query objects, auth, timeouts, retries with
|
|
270
|
+
`Retry-After`, hooks, clients (`create`/`extend`), downloads with progress.
|
|
271
|
+
- `nc.env`: `.env` parser and loader, typed getters (`number`, `bool`, `port`, `url`, `duration`, `list`,
|
|
272
|
+
`json`, `oneOf`) and `validate()` reporting every invalid variable at once. `NODE_COMFORT_DOTENV=false`
|
|
273
|
+
disables the automatic `.env` loading.
|
|
274
|
+
- `nc.errors`: `NodeComfortError`, `AssertionError`, `TimeoutError`, `AbortError`, `ValidationError`,
|
|
275
|
+
`HttpError`, `JWTError`, `ProcessError`, `SQLiteError`, each with a stable `code`.
|
|
276
|
+
- `nc.Cache`: LRU cache with TTL, `getOrSet` (concurrent loads shared), `wrap`, `prune`, `stats`.
|
|
277
|
+
- `nc.Emitter`: typed event emitter with `once`, `onAny`, `emitAsync`, `waitFor`, `iterate`.
|
|
278
|
+
|
|
279
|
+
**Existing modules**
|
|
280
|
+
- Logger: `trace`, `fatal` and `silent` levels, `LOG_LEVEL`, `LOG_FORMAT=json`, structured fields,
|
|
281
|
+
`createLogger` with scope, file output with rotation, `child` (its `fields` are added to the parent's,
|
|
282
|
+
for per-request loggers), `table`, `box`, `divider`, markup colors in
|
|
283
|
+
hex and RGB, `setLevel`, `configure`; every function returns the logger for chaining.
|
|
284
|
+
- FS: `glob` (relative patterns, or absolute ones that search where they point), `find`, `readLines`,
|
|
285
|
+
`writeFile` (atomic), `ensureFolder`, `ensureFile`, `touch`, `remove`,
|
|
286
|
+
`copy`, `move`, `isFile`, `isFolder`, `folderSize`, `tempFolder`, `sanitizeFilename`, chunked
|
|
287
|
+
`hashFile`, `readJSON` with comments, atomic `writeJSON`, debounced `watch`.
|
|
288
|
+
- Checker: about 30 new guards (`isIP`, `isPort`, `isSemver`, `isJWT`, `isHexColor`, `isBase64`,
|
|
289
|
+
`isCreditCard`, `isArrayOf`, `isOneOf`, `isClass`, `isBlank`...), `assert` and `assertType`.
|
|
290
|
+
- `str`, `num`, `arr`, `obj`, `func`, `time`, `id`: more than 120 new functions, among them `str.dedent`,
|
|
291
|
+
`str.wrap`, `str.plural`, `str.closest`, `str.mask`, `num.currency`, `num.percentile`, `num.parseBytes`,
|
|
292
|
+
`arr.paginate`, `arr.windows`, `arr.cartesian`, `obj.diff`, `obj.mergeWith`, `func.pipe`,
|
|
293
|
+
`time.setLocale`/`time.setTimezone` (every date function in any language and time zone),
|
|
294
|
+
`time.calendar`, `time.cron`, `time.every`, `time.nextRun`, `id.uuidv7`, `id.snowflake`.
|
|
295
|
+
- `num.setLocale()` sets the default language of `format`, `currency`, `percent`, `abbreviate`,
|
|
296
|
+
`ordinal` and `formatBytes`, like `time.setLocale()` does for dates.
|
|
297
|
+
- SQLite: rich `where` operators (`gt`, `in`, `like`, `between`, `$or`...), `insertMany`, `exists`,
|
|
298
|
+
`tables`, `columns`, `hasTable`, `table()` handles, `migrate()` (versioned with `user_version`),
|
|
299
|
+
`backup()`, `vacuum()`, `iterate()`, `fn()` (SQL functions in JavaScript), nested transactions with
|
|
300
|
+
savepoints, prepared statement cache, `readOnly`/`busyTimeout` options, `using` support.
|
|
301
|
+
|
|
302
|
+
### Fixed
|
|
303
|
+
|
|
304
|
+
- `const { merge } = nc.obj` (and any destructured helper) threw because functions relied on `this`.
|
|
305
|
+
- `titleCase()` broke words with accented letters.
|
|
306
|
+
- Logger groups were not indented consistently, and logging a circular object crashed.
|
|
307
|
+
- `unescapeHTML()` now decodes numeric entities (`'`, `'`) and ` `.
|
|
308
|
+
- `memoize()` no longer keeps rejected promises: a failed call is retried next time.
|
|
309
|
+
- `watch()` follows the real path of what it watches, so a short (8.3) or symlinked path no longer
|
|
310
|
+
crashes the watcher on Windows.
|
|
311
|
+
- Dates, maps, sets and plain objects coming from a worker or a `vm` context are recognized by
|
|
312
|
+
`obj`, `schema`, `time`, `checker.isPlainObject` and `JSONString` (1.x compared prototypes, which
|
|
313
|
+
are not shared between realms).
|
|
314
|
+
|
|
315
|
+
### Migration checklist
|
|
316
|
+
|
|
317
|
+
1. Upgrade Node.js to 22.13+.
|
|
318
|
+
2. Replace deep imports by the namespaces (`nc.logger`, `nc.fs`...).
|
|
319
|
+
3. SQLite: replace `if (result.error)` checks by `try/catch`, add `{ limit, orderBy, direction: "DESC" }`
|
|
320
|
+
where you relied on the old `getAll()` defaults, and check code that compared `BOOLEAN` columns to `1`/`0`.
|
|
321
|
+
4. If you parse your application's output, remember that warnings and errors now go to stderr.
|
|
322
|
+
5. Replace `nc.id.hash/hmac/safeEqual` by `nc.crypto.hash/hmac/safeEqual` (optional until 3.0).
|
|
323
|
+
|
|
324
|
+
## [1.2.0]
|
|
325
|
+
|
|
326
|
+
### Added
|
|
327
|
+
|
|
328
|
+
- Namespaces `str`, `num`, `arr`, `obj`, `func`, `time` and `id`.
|
|
329
|
+
- Checker: 22 new guards (`isEmail`, `isURL`, `isUUID`, `isEmpty`, `isPlainObject`...).
|
|
330
|
+
- Logger: `info`, `success`, `warn`, `error` and `debug` levels.
|
|
331
|
+
- FS: `readJSON`, `writeJSON`, `appendFile`, `exists`, `stat`, `fileSize`, `hashFile`, `emptyFolder`.
|
|
332
|
+
- SQLite: `:memory:` databases.
|
|
333
|
+
|
|
334
|
+
### Fixed
|
|
335
|
+
|
|
336
|
+
- SQLite: the folder of the database was created in one place and the file opened in another.
|
|
337
|
+
|
|
338
|
+
[2.1.0]: https://github.com/ix-xs/node-comfort/compare/v2.0.0...v2.1.0
|
|
339
|
+
[2.0.0]: https://github.com/ix-xs/node-comfort/compare/v1.2.0...v2.0.0
|
|
340
|
+
[1.2.0]: https://github.com/ix-xs/node-comfort/releases/tag/v1.2.0
|