melperjs 16.0.0 → 16.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/README.md +3 -3
- package/docs/docs.md +27 -0
- package/docs/index.md +355 -19
- package/docs/node.md +9 -10
- package/lib/cjs/index.cjs +15 -14
- package/lib/cjs/node.cjs +7 -6
- package/lib/esm/index.mjs +15 -14
- package/lib/esm/node.mjs +8 -7
- package/package.json +2 -2
- package/docs/general.md +0 -363
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@ npm i melperjs
|
|
|
13
13
|
|
|
14
14
|
## Documentation
|
|
15
15
|
|
|
16
|
-
Full API reference lives in the [docs folder](docs/
|
|
16
|
+
Full API reference lives in the [docs folder](docs/docs.md):
|
|
17
17
|
|
|
18
|
-
- [General Functions](docs/
|
|
19
|
-
- [Node.js Functions](docs/node.md) — `melperjs/node`
|
|
18
|
+
- [General Functions](docs/index.md) — browser-safe helpers (`melperjs`)
|
|
19
|
+
- [Node.js Functions](docs/node.md) — Node-only helpers (`melperjs/node`)
|
|
20
20
|
|
|
21
21
|
## License
|
|
22
22
|
|
package/docs/docs.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
`melperjs` is a small utility library split into two entry points:
|
|
4
|
+
|
|
5
|
+
- **Core module** (`melperjs`) — browser-safe helpers. No Node-only APIs, no `crypto`, no `fs`. Safe to import from any
|
|
6
|
+
JavaScript environment.
|
|
7
|
+
- **Node module** (`melperjs/node`) — Node.js-specific helpers built on `crypto`, `fs`, `child_process`, and `os`.
|
|
8
|
+
Importing this in a browser will fail.
|
|
9
|
+
|
|
10
|
+
## Usage
|
|
11
|
+
|
|
12
|
+
```javascript
|
|
13
|
+
// ES Module
|
|
14
|
+
import * as helper from "melperjs";
|
|
15
|
+
import * as nodeHelper from "melperjs/node";
|
|
16
|
+
|
|
17
|
+
// CommonJS
|
|
18
|
+
const helper = require("melperjs");
|
|
19
|
+
const nodeHelper = require("melperjs/node");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Both forms are supported via dual ESM/CJS builds.
|
|
23
|
+
|
|
24
|
+
## Sections
|
|
25
|
+
|
|
26
|
+
- [General Functions](index.md) — browser-safe helpers (`melperjs`)
|
|
27
|
+
- [Node.js Functions](./node.md) — Node-only helpers (`melperjs/node`)
|
package/docs/index.md
CHANGED
|
@@ -1,27 +1,363 @@
|
|
|
1
|
-
#
|
|
1
|
+
# General Functions
|
|
2
2
|
|
|
3
|
-
`melperjs
|
|
3
|
+
Browser-safe utilities exported from `melperjs`. No Node.js APIs are used here — every function runs in the browser and in Node.js without polyfills.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
JavaScript environment.
|
|
7
|
-
- **Node module** (`melperjs/node`) — Node.js-specific helpers built on `crypto`, `fs`, `child_process`, and `os`.
|
|
8
|
-
Importing this in a browser will fail.
|
|
5
|
+
## Constants
|
|
9
6
|
|
|
10
|
-
|
|
7
|
+
`CONSTANTS` bundles a few character sets and integer bounds that other functions in this module rely on. Useful when you need the same alphabets in your own code.
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
- `CONSTANTS.LOWER_CASE` — lowercase ASCII letters (`a-z`).
|
|
10
|
+
- `CONSTANTS.UPPER_CASE` — uppercase ASCII letters (`A-Z`).
|
|
11
|
+
- `CONSTANTS.HEXADECIMAL` — hex digits (`0-9a-f`).
|
|
12
|
+
- `CONSTANTS.NUMBERS` — decimal digits (`0-9`).
|
|
13
|
+
- `CONSTANTS.INT32_MIN` — `-2147483648`.
|
|
14
|
+
- `CONSTANTS.INT32_MAX` — `2147483647`.
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
const helper = require("melperjs");
|
|
19
|
-
const nodeHelper = require("melperjs/node");
|
|
20
|
-
```
|
|
16
|
+
## Errors
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
### Exception(message, response = {}, name = null)
|
|
23
19
|
|
|
24
|
-
|
|
20
|
+
Builds a standard `Error` with two extra attached fields (`response`, custom `name`) so error handlers can carry HTTP-style context without subclassing. A null/empty `response` is normalized to `{}`.
|
|
25
21
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
22
|
+
- **Parameters:**
|
|
23
|
+
- `message` (String): Human-readable error message.
|
|
24
|
+
- `response` (Object): Arbitrary payload attached as `error.response`.
|
|
25
|
+
- `name` (String|null): Overrides `error.name`. Defaults to `"Exception"`.
|
|
26
|
+
- **Returns:** `Error` with `.response` and `.name` populated.
|
|
27
|
+
|
|
28
|
+
## Time & Async
|
|
29
|
+
|
|
30
|
+
### time()
|
|
31
|
+
|
|
32
|
+
Current Unix timestamp in seconds (integer).
|
|
33
|
+
|
|
34
|
+
- **Returns:** Seconds since the epoch.
|
|
35
|
+
|
|
36
|
+
### sleepMs(milliseconds)
|
|
37
|
+
|
|
38
|
+
Promise that resolves after a delay, measured in milliseconds.
|
|
39
|
+
|
|
40
|
+
- **Parameters:**
|
|
41
|
+
- `milliseconds` (Number): Delay in ms.
|
|
42
|
+
- **Returns:** `Promise<void>`.
|
|
43
|
+
|
|
44
|
+
### sleep(seconds)
|
|
45
|
+
|
|
46
|
+
Same as `sleepMs` but the delay is given in seconds.
|
|
47
|
+
|
|
48
|
+
- **Parameters:**
|
|
49
|
+
- `seconds` (Number): Delay in seconds.
|
|
50
|
+
- **Returns:** `Promise<void>`.
|
|
51
|
+
|
|
52
|
+
### promiseTimeout(milliseconds, promise)
|
|
53
|
+
|
|
54
|
+
Races a promise against a timer. If the promise doesn't settle in time the result rejects; either way the timer is cleared.
|
|
55
|
+
|
|
56
|
+
- **Parameters:**
|
|
57
|
+
- `milliseconds` (Number): Maximum wait time before rejecting.
|
|
58
|
+
- `promise` (Promise): The work to await.
|
|
59
|
+
- **Returns:** Settles with the inner promise's value, or rejects with `Error("Promise timed out after Xms")`.
|
|
60
|
+
|
|
61
|
+
### promiseSilent(promise)
|
|
62
|
+
|
|
63
|
+
Awaits a promise but swallows both the resolved value and any rejection. Handy for fire-and-forget work where you only care about the side effects.
|
|
64
|
+
|
|
65
|
+
- **Parameters:**
|
|
66
|
+
- `promise` (Promise): The promise to consume silently.
|
|
67
|
+
- **Returns:** `Promise<undefined>` that always resolves.
|
|
68
|
+
|
|
69
|
+
### forever(delayMs, task, onError = null, onFinally = null)
|
|
70
|
+
|
|
71
|
+
Runs `task` in an infinite loop with a delay between iterations. Errors are routed to `onError`; `onFinally` runs after every iteration regardless of outcome. Any of the three callbacks can return a new positive number to update `delayMs` on the fly (useful for adaptive polling).
|
|
72
|
+
|
|
73
|
+
Errors thrown from `task` are caught and routed to `onError`; the loop keeps running. Errors thrown from `onError` propagate out and stop the loop — useful for soft shutdown by throwing on a stop signal. Errors thrown from `onFinally` are caught and ignored so that observability/cleanup failures cannot kill the worker.
|
|
74
|
+
|
|
75
|
+
- **Parameters:**
|
|
76
|
+
- `delayMs` (Number): Initial delay in milliseconds between iterations. Must be a positive finite number.
|
|
77
|
+
- `task` (Function): Async function to invoke each iteration. Exceptions are caught and forwarded to `onError`.
|
|
78
|
+
- `onError` (Function): Called with the caught error when `task` throws. Throwing from here aborts the loop.
|
|
79
|
+
- `onFinally` (Function): Called after every iteration (success or failure). Errors thrown here are swallowed.
|
|
80
|
+
- **Returns:** Promise that never resolves on its own; it rejects when `delayMs` validation fails or when `onError` throws.
|
|
81
|
+
- **Throws:** When `delayMs` is not a positive finite number.
|
|
82
|
+
|
|
83
|
+
### retry(task, maxAttempts = 1, onError = null, {delayMs = 0, backoffFactor = 1} = {})
|
|
84
|
+
|
|
85
|
+
Calls `task` up to `maxAttempts` times, returning the first successful result. Optionally waits between retries with an exponential backoff (delay grows by `delayMs * backoffFactor^(attempt-1)`).
|
|
86
|
+
|
|
87
|
+
- **Parameters:**
|
|
88
|
+
- `task` (Function): Async function to attempt.
|
|
89
|
+
- `maxAttempts` (Number): Total attempt count (1 = no retries). Default `1`.
|
|
90
|
+
- `onError` (Function): Called as `(attempt, error)` after each failed attempt.
|
|
91
|
+
- `options.delayMs` (Number): Base delay between retries in ms. `0` disables delay.
|
|
92
|
+
- `options.backoffFactor` (Number): Multiplier applied per attempt. `1` keeps delay constant; `2` doubles each retry.
|
|
93
|
+
- **Returns:** The first non-throwing result of `task`.
|
|
94
|
+
- **Throws:** The last error after `maxAttempts` failures.
|
|
95
|
+
|
|
96
|
+
## Strings
|
|
97
|
+
|
|
98
|
+
### isValidURL(url)
|
|
99
|
+
|
|
100
|
+
Tests whether the input parses as a valid URL via the `URL` constructor.
|
|
101
|
+
|
|
102
|
+
- **Parameters:**
|
|
103
|
+
- `url` (String): Candidate URL.
|
|
104
|
+
- **Returns:** `Boolean`.
|
|
105
|
+
|
|
106
|
+
### splitTrim(string, separator = null)
|
|
107
|
+
|
|
108
|
+
Splits a string, trims each piece, and drops empty results. Default separator is `\r?\n` (any newline).
|
|
109
|
+
|
|
110
|
+
- **Parameters:**
|
|
111
|
+
- `string` (String): Source text.
|
|
112
|
+
- `separator` (String|RegExp|null): Custom delimiter; `null` falls back to newlines.
|
|
113
|
+
- **Returns:** Array of non-empty trimmed strings.
|
|
114
|
+
|
|
115
|
+
### pascalCase(string)
|
|
116
|
+
|
|
117
|
+
Converts arbitrary text to `PascalCase` (uses lodash internally).
|
|
118
|
+
|
|
119
|
+
- **Parameters:**
|
|
120
|
+
- `string` (String): Input text.
|
|
121
|
+
- **Returns:** PascalCase string.
|
|
122
|
+
|
|
123
|
+
### titleCase(string, separator = " ")
|
|
124
|
+
|
|
125
|
+
Capitalizes the first letter of each word delimited by `separator`. Other characters are preserved as-is.
|
|
126
|
+
|
|
127
|
+
- **Parameters:**
|
|
128
|
+
- `string` (String): Input text.
|
|
129
|
+
- `separator` (String): Word boundary. Defaults to a single space.
|
|
130
|
+
- **Returns:** Title-cased string.
|
|
131
|
+
|
|
132
|
+
### limitString(string, limit = 35, omission = "...")
|
|
133
|
+
|
|
134
|
+
Truncates a string if it exceeds `limit` characters and appends `omission`. Strings shorter than the limit are returned unchanged.
|
|
135
|
+
|
|
136
|
+
- **Parameters:**
|
|
137
|
+
- `string` (String): Input text.
|
|
138
|
+
- `limit` (Number): Maximum length of the result (including `omission`).
|
|
139
|
+
- `omission` (String): Suffix used when truncation happens.
|
|
140
|
+
- **Returns:** Possibly truncated string.
|
|
141
|
+
|
|
142
|
+
### safeString(string)
|
|
143
|
+
|
|
144
|
+
Strips HTML tags via the `xss` library and additionally removes the body of dangerous block tags (`<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<form>`) so leftover CSS/markup cannot leak as text. CSS attribute sanitization is also disabled (no `<style>` attribute support). Intended for rendering untrusted text safely; not a substitute for a full HTML sanitizer like DOMPurify.
|
|
145
|
+
|
|
146
|
+
- **Parameters:**
|
|
147
|
+
- `string` (String): Untrusted text.
|
|
148
|
+
- **Returns:** Sanitized string with no allowed tags.
|
|
149
|
+
|
|
150
|
+
### shuffleString(string)
|
|
151
|
+
|
|
152
|
+
Randomly reorders the characters in a string using lodash's `shuffle` (Fisher-Yates).
|
|
153
|
+
|
|
154
|
+
- **Parameters:**
|
|
155
|
+
- `string` (String): Source string.
|
|
156
|
+
- **Returns:** Shuffled string of the same length.
|
|
157
|
+
|
|
158
|
+
## Random (non-cryptographic, Math.random)
|
|
159
|
+
|
|
160
|
+
### randomBoolean()
|
|
161
|
+
|
|
162
|
+
Returns a random `true` or `false` with uniform probability.
|
|
163
|
+
|
|
164
|
+
- **Returns:** `Boolean`.
|
|
165
|
+
|
|
166
|
+
### randomString(length, useNumbers = true, useUppercase = false)
|
|
167
|
+
|
|
168
|
+
Generates a random string from a configurable character set.
|
|
169
|
+
|
|
170
|
+
- **Parameters:**
|
|
171
|
+
- `length` (Number): Output length.
|
|
172
|
+
- `useNumbers` (Boolean): Include digits `0-9`.
|
|
173
|
+
- `useUppercase` (Boolean): Include uppercase letters.
|
|
174
|
+
- **Returns:** Random string.
|
|
175
|
+
|
|
176
|
+
### randomHex(length)
|
|
177
|
+
|
|
178
|
+
Generates a random hexadecimal string.
|
|
179
|
+
|
|
180
|
+
- **Parameters:**
|
|
181
|
+
- `length` (Number): Output length.
|
|
182
|
+
- **Returns:** Hex string of the requested length.
|
|
183
|
+
|
|
184
|
+
### randomInteger(min, max)
|
|
185
|
+
|
|
186
|
+
Returns a random integer in `[min, max)`. If called with a single argument it is treated as `max` with `min = 0` (e.g., `randomInteger(10)` returns `0..9`).
|
|
187
|
+
|
|
188
|
+
- **Parameters:**
|
|
189
|
+
- `min` (Number): Inclusive lower bound, or `max` when called with one argument.
|
|
190
|
+
- `max` (Number): Exclusive upper bound.
|
|
191
|
+
- **Returns:** Integer in `[min, max)`.
|
|
192
|
+
- **Throws:** When inputs are not numbers, or when `max <= min`.
|
|
193
|
+
|
|
194
|
+
### randomUuid(useDashes = true)
|
|
195
|
+
|
|
196
|
+
Generates a random UUID v4-shaped string. Not suitable for security tokens; use `secureRandomUuid` instead.
|
|
197
|
+
|
|
198
|
+
- **Parameters:**
|
|
199
|
+
- `useDashes` (Boolean): When `false`, dashes are stripped.
|
|
200
|
+
- **Returns:** UUID string.
|
|
201
|
+
|
|
202
|
+
### randomWeighted(object)
|
|
203
|
+
|
|
204
|
+
Picks a random key from `object` with probability proportional to its weight. Returns `undefined` for empty or nullish input.
|
|
205
|
+
|
|
206
|
+
- **Parameters:**
|
|
207
|
+
- `object` (Object): Map of key → positive weight.
|
|
208
|
+
- **Returns:** Selected key, or `undefined`.
|
|
209
|
+
|
|
210
|
+
### randomElement(object)
|
|
211
|
+
|
|
212
|
+
Picks a random value from an array or from an object's own enumerable values. Returns `undefined` for empty or nullish input.
|
|
213
|
+
|
|
214
|
+
- **Parameters:**
|
|
215
|
+
- `object` (Array|Object): Source collection.
|
|
216
|
+
- **Returns:** A random value, or `undefined`.
|
|
217
|
+
|
|
218
|
+
## Deterministic Random (seeded)
|
|
219
|
+
|
|
220
|
+
### mulberry32(seed)
|
|
221
|
+
|
|
222
|
+
Returns a deterministic PRNG (Mulberry32) seeded by a 32-bit integer or by a string (hashed internally to 32 bits). Each call to the returned function produces a `[0, 1)` float. Very fast, but only 32 bits of state — not for cryptographic use.
|
|
223
|
+
|
|
224
|
+
- **Parameters:**
|
|
225
|
+
- `seed` (Number|String): Seed value.
|
|
226
|
+
- **Returns:** Function that returns a `Number` in `[0, 1)` per call.
|
|
227
|
+
|
|
228
|
+
### seedHex(seed, length)
|
|
229
|
+
|
|
230
|
+
Builds a deterministic hex string of the requested length from a seed via `mulberry32`. Same seed always yields the same output. Useful for short, repeatable identifiers (e.g., proxy session stickiness).
|
|
231
|
+
|
|
232
|
+
- **Parameters:**
|
|
233
|
+
- `seed` (Any): Seed value (coerced to string).
|
|
234
|
+
- `length` (Number): Output length in hex characters. Required.
|
|
235
|
+
- **Returns:** Hex string.
|
|
236
|
+
|
|
237
|
+
## Predicates
|
|
238
|
+
|
|
239
|
+
### checkEmpty(value)
|
|
240
|
+
|
|
241
|
+
Like lodash's `isEmpty` but additionally treats `0` (numeric zero) as empty.
|
|
242
|
+
|
|
243
|
+
- **Parameters:**
|
|
244
|
+
- `value` (Any): Value to test.
|
|
245
|
+
- **Returns:** `Boolean`.
|
|
246
|
+
|
|
247
|
+
### isInt32(value)
|
|
248
|
+
|
|
249
|
+
Tests whether `value` is an integer within the signed 32-bit range.
|
|
250
|
+
|
|
251
|
+
- **Parameters:**
|
|
252
|
+
- `value` (Any): Value to test.
|
|
253
|
+
- **Returns:** `Boolean`.
|
|
254
|
+
|
|
255
|
+
### isPositiveNumber(value)
|
|
256
|
+
|
|
257
|
+
Tests whether `value` is a finite positive number (excludes `NaN`, `Infinity`, non-numbers, `0`, and negatives).
|
|
258
|
+
|
|
259
|
+
- **Parameters:**
|
|
260
|
+
- `value` (Any): Value to test.
|
|
261
|
+
- **Returns:** `Boolean`.
|
|
262
|
+
|
|
263
|
+
## Objects
|
|
264
|
+
|
|
265
|
+
### coerceObjectNumbers(object)
|
|
266
|
+
|
|
267
|
+
Walks an object's own enumerable keys and converts string values that match a numeric pattern (e.g., `"1.5"`, `"-3"`, `"1e3"`) to `Number`. Non-string values and non-strict numeric strings (e.g., `"12abc"`, `"1,000"`) are left untouched. Mutates the input.
|
|
268
|
+
|
|
269
|
+
- **Parameters:**
|
|
270
|
+
- `object` (Object): Object to coerce in place.
|
|
271
|
+
- **Returns:** The same `object`.
|
|
272
|
+
|
|
273
|
+
### coerceObjectIntegers(object)
|
|
274
|
+
|
|
275
|
+
Same as `coerceObjectNumbers` but only converts whole integer strings via `parseInt` (e.g., `"002"` → `2`, `"-7"` → `-7`). Mutates the input.
|
|
276
|
+
|
|
277
|
+
- **Parameters:**
|
|
278
|
+
- `object` (Object): Object to coerce in place.
|
|
279
|
+
- **Returns:** The same `object`.
|
|
280
|
+
|
|
281
|
+
### findNodeByKey(key, node, pair = null)
|
|
282
|
+
|
|
283
|
+
Depth-first search through a nested object for the first node that owns `key`. If `pair` is provided, the node's value at `key` must equal `pair` (strict equality, supports falsy values like `false` / `0` / `""`).
|
|
284
|
+
|
|
285
|
+
- **Parameters:**
|
|
286
|
+
- `key` (String): Property name to find.
|
|
287
|
+
- `node` (Object): Tree to search.
|
|
288
|
+
- `pair` (Any): Optional value constraint. `null` means "match any value".
|
|
289
|
+
- **Returns:** The matching node, or `null` if not found.
|
|
290
|
+
|
|
291
|
+
### waitForProperty(object, property, timeout = 5000, interval = 100)
|
|
292
|
+
|
|
293
|
+
Polls `object` until it owns `property`, then resolves with the property's value. Rejects after `timeout` milliseconds.
|
|
294
|
+
|
|
295
|
+
- **Parameters:**
|
|
296
|
+
- `object` (Object): Object to watch.
|
|
297
|
+
- `property` (String): Property name to wait for.
|
|
298
|
+
- `timeout` (Number): Maximum wait time in milliseconds.
|
|
299
|
+
- `interval` (Number): Poll interval in milliseconds.
|
|
300
|
+
- **Returns:** `Promise` resolving to the property's value.
|
|
301
|
+
- **Throws:** When the property does not appear within `timeout`.
|
|
302
|
+
|
|
303
|
+
### shuffleObject(object)
|
|
304
|
+
|
|
305
|
+
Returns a new object whose entries are in random order (the underlying iteration order is the only thing being shuffled).
|
|
306
|
+
|
|
307
|
+
- **Parameters:**
|
|
308
|
+
- `object` (Object): Source object.
|
|
309
|
+
- **Returns:** New object with the same keys/values in shuffled order.
|
|
310
|
+
|
|
311
|
+
### objectStringify(object)
|
|
312
|
+
|
|
313
|
+
Recursively walks an object and converts every leaf value to `String(value)`. Nested objects and arrays are descended into; mutates the input.
|
|
314
|
+
|
|
315
|
+
- **Parameters:**
|
|
316
|
+
- `object` (Object): Object to mutate.
|
|
317
|
+
- **Returns:** The same `object`.
|
|
318
|
+
|
|
319
|
+
## Cookies
|
|
320
|
+
|
|
321
|
+
### cookiesFromResponse(response, decodeValues = false)
|
|
322
|
+
|
|
323
|
+
Parses the `Set-Cookie` headers off a response-like object (compatible with Node `http`, fetch responses, or anything with `headers["set-cookie"]`) and returns a flat `{name: value}` map via `set-cookie-parser`.
|
|
324
|
+
|
|
325
|
+
- **Parameters:**
|
|
326
|
+
- `response` (Object): Response-like with parsed headers.
|
|
327
|
+
- `decodeValues` (Boolean): Whether to URL-decode cookie values.
|
|
328
|
+
- **Returns:** Map of cookie name → value.
|
|
329
|
+
|
|
330
|
+
### cookiesToHeader(cookies)
|
|
331
|
+
|
|
332
|
+
Serializes a `{name: value}` map into a `Cookie:` header string (`name=value` pairs joined with `"; "`). Null/undefined values are dropped.
|
|
333
|
+
|
|
334
|
+
- **Parameters:**
|
|
335
|
+
- `cookies` (Object): Map of cookie name → value.
|
|
336
|
+
- **Returns:** Cookie header string, or `""` for empty/missing input.
|
|
337
|
+
|
|
338
|
+
### cookiesFromHeader(header)
|
|
339
|
+
|
|
340
|
+
Parses a single `Cookie:` header string into a `{name: value}` map. Pieces without `=` are skipped; multiple `=` inside a value are preserved.
|
|
341
|
+
|
|
342
|
+
- **Parameters:**
|
|
343
|
+
- `header` (String): Cookie header value.
|
|
344
|
+
- **Returns:** Map of cookie name → value. Empty input returns `{}`.
|
|
345
|
+
|
|
346
|
+
## HTTP Helpers
|
|
347
|
+
|
|
348
|
+
### isTransientHttpCode(httpCode)
|
|
349
|
+
|
|
350
|
+
Flags HTTP status codes that are typically transient or worth retrying (missing/`NaN`, `100`, `402`, `407`, `460-469`, anything `≥ 500`).
|
|
351
|
+
|
|
352
|
+
- **Parameters:**
|
|
353
|
+
- `httpCode` (Number|null|undefined): Status code to inspect.
|
|
354
|
+
- **Returns:** `Boolean`.
|
|
355
|
+
|
|
356
|
+
### getResponseError(error, limit = 200)
|
|
357
|
+
|
|
358
|
+
Extracts a short error description from an HTTP error-like object. Prefers `error.response.status|error.response.data`, then `error.response.data`, then `error.message`. Truncates the result to `limit` characters via `limitString`.
|
|
359
|
+
|
|
360
|
+
- **Parameters:**
|
|
361
|
+
- `error` (Error): Error from an HTTP client.
|
|
362
|
+
- `limit` (Number): Maximum length of the returned string.
|
|
363
|
+
- **Returns:** Trimmed error description.
|
package/docs/node.md
CHANGED
|
@@ -7,11 +7,11 @@ and will not run in a browser.
|
|
|
7
7
|
|
|
8
8
|
All `secureRandom*` helpers use Node's `crypto` module (`crypto.randomInt`, `crypto.randomBytes`, `crypto.randomUUID`).
|
|
9
9
|
Use these for session tokens, API keys, nonces, and anything security-sensitive. For non-secure / faster equivalents,
|
|
10
|
-
see the `random*` family in [General Functions](
|
|
10
|
+
see the `random*` family in [General Functions](index.md).
|
|
11
11
|
|
|
12
12
|
### secureRandomBoolean()
|
|
13
13
|
|
|
14
|
-
Returns `true` or `false` with
|
|
14
|
+
Returns a cryptographically random `true` or `false` with uniform probability.
|
|
15
15
|
|
|
16
16
|
- **Returns:** `Boolean`.
|
|
17
17
|
|
|
@@ -35,16 +35,17 @@ Generates a cryptographically random hexadecimal string.
|
|
|
35
35
|
|
|
36
36
|
### secureRandomInteger(min, max)
|
|
37
37
|
|
|
38
|
-
Returns a cryptographically random integer in `[min, max)`.
|
|
38
|
+
Returns a cryptographically random integer in `[min, max)`. If called with a single argument it is treated as `max` with `min = 0` (e.g., `secureRandomInteger(10)` returns `0..9`).
|
|
39
39
|
|
|
40
40
|
- **Parameters:**
|
|
41
|
-
- `min` (Number): Inclusive lower bound.
|
|
41
|
+
- `min` (Number): Inclusive lower bound, or `max` when called with one argument.
|
|
42
42
|
- `max` (Number): Exclusive upper bound.
|
|
43
43
|
- **Returns:** Integer in `[min, max)`.
|
|
44
|
+
- **Throws:** When inputs are not numbers, or when `max <= min`.
|
|
44
45
|
|
|
45
46
|
### secureRandomUuid(useDashes = true)
|
|
46
47
|
|
|
47
|
-
Generates a cryptographically random UUID v4
|
|
48
|
+
Generates a cryptographically random UUID v4 string. Suitable for security tokens.
|
|
48
49
|
|
|
49
50
|
- **Parameters:**
|
|
50
51
|
- `useDashes` (Boolean): When `false`, dashes are stripped.
|
|
@@ -52,17 +53,15 @@ Generates a cryptographically random UUID v4 via `crypto.randomUUID()`. Suitable
|
|
|
52
53
|
|
|
53
54
|
### secureRandomWeighted(object)
|
|
54
55
|
|
|
55
|
-
Picks a key from `object` with probability proportional to its weight
|
|
56
|
-
must be positive integers.
|
|
56
|
+
Picks a cryptographically random key from `object` with probability proportional to its weight. Returns `undefined` for empty or nullish input.
|
|
57
57
|
|
|
58
58
|
- **Parameters:**
|
|
59
59
|
- `object` (Object): Map of key → positive integer weight.
|
|
60
|
-
- **Returns:** Selected key
|
|
60
|
+
- **Returns:** Selected key, or `undefined`.
|
|
61
61
|
|
|
62
62
|
### secureRandomElement(object)
|
|
63
63
|
|
|
64
|
-
Picks a cryptographically random value from an array or from an object's own enumerable values. Returns `undefined` for
|
|
65
|
-
empty or nullish input.
|
|
64
|
+
Picks a cryptographically random value from an array or from an object's own enumerable values. Returns `undefined` for empty or nullish input.
|
|
66
65
|
|
|
67
66
|
- **Parameters:**
|
|
68
67
|
- `object` (Array|Object): Source collection.
|
package/lib/cjs/index.cjs
CHANGED
|
@@ -89,33 +89,33 @@ function promiseSilent(promise) {
|
|
|
89
89
|
}
|
|
90
90
|
async function forever(delayMs, task, onError = null, onFinally = null) {
|
|
91
91
|
if (!isPositiveNumber(delayMs)) throw new Error("delayMs must be a positive number");
|
|
92
|
-
const
|
|
92
|
+
const update = value => {
|
|
93
93
|
if (isPositiveNumber(value)) delayMs = value;
|
|
94
94
|
};
|
|
95
95
|
while (true) {
|
|
96
96
|
try {
|
|
97
|
-
|
|
97
|
+
update(await task());
|
|
98
98
|
} catch (error) {
|
|
99
|
-
if (onError)
|
|
99
|
+
if (onError) update(await onError(error));
|
|
100
100
|
} finally {
|
|
101
101
|
if (onFinally) {
|
|
102
102
|
try {
|
|
103
|
-
|
|
103
|
+
update(await onFinally());
|
|
104
104
|
} catch {}
|
|
105
105
|
}
|
|
106
106
|
await sleepMs(delayMs);
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
async function retry(
|
|
110
|
+
async function retry(task, maxAttempts = 1, onError = null, {
|
|
111
111
|
delayMs = 0,
|
|
112
112
|
backoffFactor = 1
|
|
113
113
|
} = {}) {
|
|
114
114
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
115
115
|
try {
|
|
116
|
-
return await
|
|
116
|
+
return await task();
|
|
117
117
|
} catch (error) {
|
|
118
|
-
if (
|
|
118
|
+
if (onError) await onError(attempt, error);
|
|
119
119
|
if (attempt >= maxAttempts) throw error;
|
|
120
120
|
if (delayMs > 0) await sleepMs(delayMs * backoffFactor ** (attempt - 1));
|
|
121
121
|
}
|
|
@@ -146,7 +146,7 @@ function isInt32(value) {
|
|
|
146
146
|
return Number.isInteger(value) && value >= CONSTANTS.INT32_MIN && value <= CONSTANTS.INT32_MAX;
|
|
147
147
|
}
|
|
148
148
|
function isPositiveNumber(value) {
|
|
149
|
-
return
|
|
149
|
+
return Number.isFinite(value) && value > 0;
|
|
150
150
|
}
|
|
151
151
|
function coerceObjectNumbers(object) {
|
|
152
152
|
for (const key of Object.keys(object)) {
|
|
@@ -191,7 +191,7 @@ function waitForProperty(object, property, timeout = 5000, interval = 100) {
|
|
|
191
191
|
resolve(object[property]);
|
|
192
192
|
} else if (Date.now() - startTime >= timeout) {
|
|
193
193
|
clearInterval(checkProperty);
|
|
194
|
-
reject(new Error(`Property "${property}" did not appear within ${timeout}
|
|
194
|
+
reject(new Error(`Property "${property}" did not appear within ${timeout}ms`));
|
|
195
195
|
}
|
|
196
196
|
}, interval);
|
|
197
197
|
});
|
|
@@ -246,7 +246,7 @@ function randomHex(length) {
|
|
|
246
246
|
}
|
|
247
247
|
return result;
|
|
248
248
|
}
|
|
249
|
-
function randomInteger(min, max) {
|
|
249
|
+
function randomInteger(min, max = undefined) {
|
|
250
250
|
if (typeof max === 'undefined') {
|
|
251
251
|
max = min;
|
|
252
252
|
min = 0;
|
|
@@ -267,6 +267,7 @@ function randomUuid(useDashes = true) {
|
|
|
267
267
|
return useDashes ? uuid : uuid.replaceAll("-", "");
|
|
268
268
|
}
|
|
269
269
|
function randomWeighted(object) {
|
|
270
|
+
if (checkEmpty(object)) return undefined;
|
|
270
271
|
const elements = Object.keys(object);
|
|
271
272
|
const weights = Object.values(object);
|
|
272
273
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
@@ -280,7 +281,7 @@ function randomWeighted(object) {
|
|
|
280
281
|
}
|
|
281
282
|
}
|
|
282
283
|
function randomElement(object) {
|
|
283
|
-
if (
|
|
284
|
+
if (checkEmpty(object)) return undefined;
|
|
284
285
|
const values = Array.isArray(object) ? object : Object.values(object);
|
|
285
286
|
if (values.length === 0) return undefined;
|
|
286
287
|
return values[Math.floor(Math.random() * values.length)];
|
|
@@ -310,14 +311,14 @@ function seedHex(seed, length) {
|
|
|
310
311
|
return result.slice(0, length);
|
|
311
312
|
}
|
|
312
313
|
function cookiesFromResponse(response, decodeValues = false) {
|
|
313
|
-
const
|
|
314
|
+
const obj = {};
|
|
314
315
|
const cookies = _setCookieParser.default.parse(response, {
|
|
315
316
|
decodeValues
|
|
316
317
|
});
|
|
317
318
|
for (const cookie of cookies) {
|
|
318
|
-
|
|
319
|
+
obj[cookie.name] = cookie.value;
|
|
319
320
|
}
|
|
320
|
-
return
|
|
321
|
+
return obj;
|
|
321
322
|
}
|
|
322
323
|
function cookiesToHeader(cookies) {
|
|
323
324
|
if (!cookies) return "";
|
package/lib/cjs/node.cjs
CHANGED
|
@@ -42,7 +42,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
42
42
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
43
43
|
const execAsync = (0, _util.promisify)(_child_process.exec);
|
|
44
44
|
function secureRandomBoolean() {
|
|
45
|
-
return
|
|
45
|
+
return secureRandomInteger(2) === 1;
|
|
46
46
|
}
|
|
47
47
|
function secureRandomString(length, useNumbers = true, useUppercase = false) {
|
|
48
48
|
let characters = _index.CONSTANTS.LOWER_CASE;
|
|
@@ -50,14 +50,14 @@ function secureRandomString(length, useNumbers = true, useUppercase = false) {
|
|
|
50
50
|
if (useNumbers) characters += _index.CONSTANTS.NUMBERS;
|
|
51
51
|
let result = '';
|
|
52
52
|
for (let i = 0; i < length; i++) {
|
|
53
|
-
result += characters[
|
|
53
|
+
result += characters[secureRandomInteger(0, characters.length)];
|
|
54
54
|
}
|
|
55
55
|
return result;
|
|
56
56
|
}
|
|
57
57
|
function secureRandomHex(length) {
|
|
58
58
|
return _crypto.default.randomBytes(Math.ceil(length / 2)).toString('hex').slice(0, length);
|
|
59
59
|
}
|
|
60
|
-
function secureRandomInteger(min, max) {
|
|
60
|
+
function secureRandomInteger(min, max = undefined) {
|
|
61
61
|
return _crypto.default.randomInt(min, max);
|
|
62
62
|
}
|
|
63
63
|
function secureRandomUuid(useDashes = true) {
|
|
@@ -65,6 +65,7 @@ function secureRandomUuid(useDashes = true) {
|
|
|
65
65
|
return useDashes ? uuid : uuid.replaceAll("-", "");
|
|
66
66
|
}
|
|
67
67
|
function secureRandomWeighted(object) {
|
|
68
|
+
if ((0, _index.checkEmpty)(object)) return undefined;
|
|
68
69
|
const elements = Object.keys(object);
|
|
69
70
|
const weights = Object.values(object);
|
|
70
71
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
@@ -78,10 +79,10 @@ function secureRandomWeighted(object) {
|
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
81
|
function secureRandomElement(object) {
|
|
81
|
-
if (
|
|
82
|
+
if ((0, _index.checkEmpty)(object)) return undefined;
|
|
82
83
|
const values = Array.isArray(object) ? object : Object.values(object);
|
|
83
84
|
if (values.length === 0) return undefined;
|
|
84
|
-
return values[
|
|
85
|
+
return values[secureRandomInteger(0, values.length)];
|
|
85
86
|
}
|
|
86
87
|
function uuidFromSeed(seed, useDashes = true) {
|
|
87
88
|
const hash = _crypto.default.createHash('md5').update(seed).digest();
|
|
@@ -174,7 +175,7 @@ function normalizeProxy(proxy, protocol = "http") {
|
|
|
174
175
|
const start = Number(parts[1]);
|
|
175
176
|
const end = Number(parts[2]);
|
|
176
177
|
if (Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end) {
|
|
177
|
-
body = `${parts[0]}:${
|
|
178
|
+
body = `${parts[0]}:${(0, _index.randomInteger)(start, end + 1)}`;
|
|
178
179
|
}
|
|
179
180
|
}
|
|
180
181
|
return `${protocol}://${auth}${body}`;
|
package/lib/esm/index.mjs
CHANGED
|
@@ -44,33 +44,33 @@ export function promiseSilent(promise) {
|
|
|
44
44
|
}
|
|
45
45
|
export async function forever(delayMs, task, onError = null, onFinally = null) {
|
|
46
46
|
if (!isPositiveNumber(delayMs)) throw new Error("delayMs must be a positive number");
|
|
47
|
-
const
|
|
47
|
+
const update = value => {
|
|
48
48
|
if (isPositiveNumber(value)) delayMs = value;
|
|
49
49
|
};
|
|
50
50
|
while (true) {
|
|
51
51
|
try {
|
|
52
|
-
|
|
52
|
+
update(await task());
|
|
53
53
|
} catch (error) {
|
|
54
|
-
if (onError)
|
|
54
|
+
if (onError) update(await onError(error));
|
|
55
55
|
} finally {
|
|
56
56
|
if (onFinally) {
|
|
57
57
|
try {
|
|
58
|
-
|
|
58
|
+
update(await onFinally());
|
|
59
59
|
} catch {}
|
|
60
60
|
}
|
|
61
61
|
await sleepMs(delayMs);
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
-
export async function retry(
|
|
65
|
+
export async function retry(task, maxAttempts = 1, onError = null, {
|
|
66
66
|
delayMs = 0,
|
|
67
67
|
backoffFactor = 1
|
|
68
68
|
} = {}) {
|
|
69
69
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
70
70
|
try {
|
|
71
|
-
return await
|
|
71
|
+
return await task();
|
|
72
72
|
} catch (error) {
|
|
73
|
-
if (
|
|
73
|
+
if (onError) await onError(attempt, error);
|
|
74
74
|
if (attempt >= maxAttempts) throw error;
|
|
75
75
|
if (delayMs > 0) await sleepMs(delayMs * backoffFactor ** (attempt - 1));
|
|
76
76
|
}
|
|
@@ -101,7 +101,7 @@ export function isInt32(value) {
|
|
|
101
101
|
return Number.isInteger(value) && value >= CONSTANTS.INT32_MIN && value <= CONSTANTS.INT32_MAX;
|
|
102
102
|
}
|
|
103
103
|
export function isPositiveNumber(value) {
|
|
104
|
-
return
|
|
104
|
+
return Number.isFinite(value) && value > 0;
|
|
105
105
|
}
|
|
106
106
|
export function coerceObjectNumbers(object) {
|
|
107
107
|
for (const key of Object.keys(object)) {
|
|
@@ -146,7 +146,7 @@ export function waitForProperty(object, property, timeout = 5000, interval = 100
|
|
|
146
146
|
resolve(object[property]);
|
|
147
147
|
} else if (Date.now() - startTime >= timeout) {
|
|
148
148
|
clearInterval(checkProperty);
|
|
149
|
-
reject(new Error(`Property "${property}" did not appear within ${timeout}
|
|
149
|
+
reject(new Error(`Property "${property}" did not appear within ${timeout}ms`));
|
|
150
150
|
}
|
|
151
151
|
}, interval);
|
|
152
152
|
});
|
|
@@ -201,7 +201,7 @@ export function randomHex(length) {
|
|
|
201
201
|
}
|
|
202
202
|
return result;
|
|
203
203
|
}
|
|
204
|
-
export function randomInteger(min, max) {
|
|
204
|
+
export function randomInteger(min, max = undefined) {
|
|
205
205
|
if (typeof max === 'undefined') {
|
|
206
206
|
max = min;
|
|
207
207
|
min = 0;
|
|
@@ -222,6 +222,7 @@ export function randomUuid(useDashes = true) {
|
|
|
222
222
|
return useDashes ? uuid : uuid.replaceAll("-", "");
|
|
223
223
|
}
|
|
224
224
|
export function randomWeighted(object) {
|
|
225
|
+
if (checkEmpty(object)) return undefined;
|
|
225
226
|
const elements = Object.keys(object);
|
|
226
227
|
const weights = Object.values(object);
|
|
227
228
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
@@ -235,7 +236,7 @@ export function randomWeighted(object) {
|
|
|
235
236
|
}
|
|
236
237
|
}
|
|
237
238
|
export function randomElement(object) {
|
|
238
|
-
if (
|
|
239
|
+
if (checkEmpty(object)) return undefined;
|
|
239
240
|
const values = Array.isArray(object) ? object : Object.values(object);
|
|
240
241
|
if (values.length === 0) return undefined;
|
|
241
242
|
return values[Math.floor(Math.random() * values.length)];
|
|
@@ -265,14 +266,14 @@ export function seedHex(seed, length) {
|
|
|
265
266
|
return result.slice(0, length);
|
|
266
267
|
}
|
|
267
268
|
export function cookiesFromResponse(response, decodeValues = false) {
|
|
268
|
-
const
|
|
269
|
+
const obj = {};
|
|
269
270
|
const cookies = setCookieParser.parse(response, {
|
|
270
271
|
decodeValues
|
|
271
272
|
});
|
|
272
273
|
for (const cookie of cookies) {
|
|
273
|
-
|
|
274
|
+
obj[cookie.name] = cookie.value;
|
|
274
275
|
}
|
|
275
|
-
return
|
|
276
|
+
return obj;
|
|
276
277
|
}
|
|
277
278
|
export function cookiesToHeader(cookies) {
|
|
278
279
|
if (!cookies) return "";
|
package/lib/esm/node.mjs
CHANGED
|
@@ -6,10 +6,10 @@ import { networkInterfaces } from "os";
|
|
|
6
6
|
import { exec, execFileSync } from "child_process";
|
|
7
7
|
import { promisify } from "util";
|
|
8
8
|
import bcrypt from "bcryptjs";
|
|
9
|
-
import { CONSTANTS, splitTrim, randomInteger, randomHex, seedHex } from "./index.mjs";
|
|
9
|
+
import { CONSTANTS, splitTrim, randomInteger, randomHex, seedHex, checkEmpty } from "./index.mjs";
|
|
10
10
|
const execAsync = promisify(exec);
|
|
11
11
|
export function secureRandomBoolean() {
|
|
12
|
-
return
|
|
12
|
+
return secureRandomInteger(2) === 1;
|
|
13
13
|
}
|
|
14
14
|
export function secureRandomString(length, useNumbers = true, useUppercase = false) {
|
|
15
15
|
let characters = CONSTANTS.LOWER_CASE;
|
|
@@ -17,14 +17,14 @@ export function secureRandomString(length, useNumbers = true, useUppercase = fal
|
|
|
17
17
|
if (useNumbers) characters += CONSTANTS.NUMBERS;
|
|
18
18
|
let result = '';
|
|
19
19
|
for (let i = 0; i < length; i++) {
|
|
20
|
-
result += characters[
|
|
20
|
+
result += characters[secureRandomInteger(0, characters.length)];
|
|
21
21
|
}
|
|
22
22
|
return result;
|
|
23
23
|
}
|
|
24
24
|
export function secureRandomHex(length) {
|
|
25
25
|
return crypto.randomBytes(Math.ceil(length / 2)).toString('hex').slice(0, length);
|
|
26
26
|
}
|
|
27
|
-
export function secureRandomInteger(min, max) {
|
|
27
|
+
export function secureRandomInteger(min, max = undefined) {
|
|
28
28
|
return crypto.randomInt(min, max);
|
|
29
29
|
}
|
|
30
30
|
export function secureRandomUuid(useDashes = true) {
|
|
@@ -32,6 +32,7 @@ export function secureRandomUuid(useDashes = true) {
|
|
|
32
32
|
return useDashes ? uuid : uuid.replaceAll("-", "");
|
|
33
33
|
}
|
|
34
34
|
export function secureRandomWeighted(object) {
|
|
35
|
+
if (checkEmpty(object)) return undefined;
|
|
35
36
|
const elements = Object.keys(object);
|
|
36
37
|
const weights = Object.values(object);
|
|
37
38
|
const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
|
|
@@ -45,10 +46,10 @@ export function secureRandomWeighted(object) {
|
|
|
45
46
|
}
|
|
46
47
|
}
|
|
47
48
|
export function secureRandomElement(object) {
|
|
48
|
-
if (
|
|
49
|
+
if (checkEmpty(object)) return undefined;
|
|
49
50
|
const values = Array.isArray(object) ? object : Object.values(object);
|
|
50
51
|
if (values.length === 0) return undefined;
|
|
51
|
-
return values[
|
|
52
|
+
return values[secureRandomInteger(0, values.length)];
|
|
52
53
|
}
|
|
53
54
|
export function uuidFromSeed(seed, useDashes = true) {
|
|
54
55
|
const hash = crypto.createHash('md5').update(seed).digest();
|
|
@@ -141,7 +142,7 @@ export function normalizeProxy(proxy, protocol = "http") {
|
|
|
141
142
|
const start = Number(parts[1]);
|
|
142
143
|
const end = Number(parts[2]);
|
|
143
144
|
if (Number.isInteger(start) && Number.isInteger(end) && start >= 0 && start <= end) {
|
|
144
|
-
body = `${parts[0]}:${
|
|
145
|
+
body = `${parts[0]}:${randomInteger(start, end + 1)}`;
|
|
145
146
|
}
|
|
146
147
|
}
|
|
147
148
|
return `${protocol}://${auth}${body}`;
|
package/package.json
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
],
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
|
-
"url": "https://github.com/mahelbir/melperjs.git"
|
|
29
|
+
"url": "git+https://github.com/mahelbir/melperjs.git"
|
|
30
30
|
},
|
|
31
31
|
"author": "Mahmuthan Elbir",
|
|
32
32
|
"license": "MIT",
|
|
@@ -70,5 +70,5 @@
|
|
|
70
70
|
"babel-plugin-transform-import-meta": "^2.3.3",
|
|
71
71
|
"cross-env": "^10.1.0"
|
|
72
72
|
},
|
|
73
|
-
"version": "16.
|
|
73
|
+
"version": "16.1.0"
|
|
74
74
|
}
|
package/docs/general.md
DELETED
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
# General Functions
|
|
2
|
-
|
|
3
|
-
Browser-safe utilities exported from `melperjs`. No Node.js APIs are used here — every function runs in the browser and in Node.js without polyfills.
|
|
4
|
-
|
|
5
|
-
## Constants
|
|
6
|
-
|
|
7
|
-
`CONSTANTS` bundles a few character sets and integer bounds that other functions in this module rely on. Useful when you need the same alphabets in your own code.
|
|
8
|
-
|
|
9
|
-
- `CONSTANTS.LOWER_CASE` — lowercase ASCII letters (`a-z`).
|
|
10
|
-
- `CONSTANTS.UPPER_CASE` — uppercase ASCII letters (`A-Z`).
|
|
11
|
-
- `CONSTANTS.HEXADECIMAL` — hex digits (`0-9a-f`).
|
|
12
|
-
- `CONSTANTS.NUMBERS` — decimal digits (`0-9`).
|
|
13
|
-
- `CONSTANTS.INT32_MIN` — `-2147483648`.
|
|
14
|
-
- `CONSTANTS.INT32_MAX` — `2147483647`.
|
|
15
|
-
|
|
16
|
-
## Errors
|
|
17
|
-
|
|
18
|
-
### Exception(message, response = {}, name = null)
|
|
19
|
-
|
|
20
|
-
Builds a standard `Error` with two extra attached fields (`response`, custom `name`) so error handlers can carry HTTP-style context without subclassing. A null/empty `response` is normalized to `{}`.
|
|
21
|
-
|
|
22
|
-
- **Parameters:**
|
|
23
|
-
- `message` (String): Human-readable error message.
|
|
24
|
-
- `response` (Object): Arbitrary payload attached as `error.response`.
|
|
25
|
-
- `name` (String|null): Overrides `error.name`. Defaults to `"Exception"`.
|
|
26
|
-
- **Returns:** `Error` with `.response` and `.name` populated.
|
|
27
|
-
|
|
28
|
-
## Time & Async
|
|
29
|
-
|
|
30
|
-
### time()
|
|
31
|
-
|
|
32
|
-
Current Unix timestamp in seconds (integer).
|
|
33
|
-
|
|
34
|
-
- **Returns:** Seconds since the epoch.
|
|
35
|
-
|
|
36
|
-
### sleepMs(milliseconds)
|
|
37
|
-
|
|
38
|
-
Promise that resolves after a delay, measured in milliseconds.
|
|
39
|
-
|
|
40
|
-
- **Parameters:**
|
|
41
|
-
- `milliseconds` (Number): Delay in ms.
|
|
42
|
-
- **Returns:** `Promise<void>`.
|
|
43
|
-
|
|
44
|
-
### sleep(seconds)
|
|
45
|
-
|
|
46
|
-
Same as `sleepMs` but the delay is given in seconds.
|
|
47
|
-
|
|
48
|
-
- **Parameters:**
|
|
49
|
-
- `seconds` (Number): Delay in seconds.
|
|
50
|
-
- **Returns:** `Promise<void>`.
|
|
51
|
-
|
|
52
|
-
### promiseTimeout(milliseconds, promise)
|
|
53
|
-
|
|
54
|
-
Races a promise against a timer. If the promise doesn't settle in time the result rejects; either way the timer is cleared.
|
|
55
|
-
|
|
56
|
-
- **Parameters:**
|
|
57
|
-
- `milliseconds` (Number): Maximum wait time before rejecting.
|
|
58
|
-
- `promise` (Promise): The work to await.
|
|
59
|
-
- **Returns:** Settles with the inner promise's value, or rejects with `Error("Promise timed out after Xms")`.
|
|
60
|
-
|
|
61
|
-
### promiseSilent(promise)
|
|
62
|
-
|
|
63
|
-
Awaits a promise but swallows both the resolved value and any rejection. Handy for fire-and-forget work where you only care about the side effects.
|
|
64
|
-
|
|
65
|
-
- **Parameters:**
|
|
66
|
-
- `promise` (Promise): The promise to consume silently.
|
|
67
|
-
- **Returns:** `Promise<undefined>` that always resolves.
|
|
68
|
-
|
|
69
|
-
### forever(delayMs, task, onError = null, onFinally = null)
|
|
70
|
-
|
|
71
|
-
Runs `task` in an infinite loop with a delay between iterations. Errors are routed to `onError`; `onFinally` runs after every iteration regardless of outcome. Any of the three callbacks can return a new positive number to update `delayMs` on the fly (useful for adaptive polling).
|
|
72
|
-
|
|
73
|
-
Errors thrown from `task` are caught and routed to `onError`; the loop keeps running. Errors thrown from `onError` propagate out and stop the loop — useful for soft shutdown by throwing on a stop signal. Errors thrown from `onFinally` are caught and ignored so that observability/cleanup failures cannot kill the worker.
|
|
74
|
-
|
|
75
|
-
- **Parameters:**
|
|
76
|
-
- `delayMs` (Number): Initial delay in milliseconds between iterations. Must be a positive finite number.
|
|
77
|
-
- `task` (Function): Async function to invoke each iteration. Exceptions are caught and forwarded to `onError`.
|
|
78
|
-
- `onError` (Function): Called with the caught error when `task` throws. Throwing from here aborts the loop.
|
|
79
|
-
- `onFinally` (Function): Called after every iteration (success or failure). Errors thrown here are swallowed.
|
|
80
|
-
- **Returns:** Promise that never resolves on its own; it rejects when `delayMs` validation fails or when `onError` throws.
|
|
81
|
-
- **Throws:** When `delayMs` is not a positive finite number.
|
|
82
|
-
|
|
83
|
-
### retry(callFn, maxAttempts, errorFn = null, {delayMs = 0, backoffFactor = 1} = {})
|
|
84
|
-
|
|
85
|
-
Calls `callFn` up to `maxAttempts` times, returning the first successful result. Optionally waits between retries with an exponential backoff (delay grows by `delayMs * backoffFactor^(attempt-1)`).
|
|
86
|
-
|
|
87
|
-
- **Parameters:**
|
|
88
|
-
- `callFn` (Function): Async function to attempt.
|
|
89
|
-
- `maxAttempts` (Number): Total attempt count (1 = no retries).
|
|
90
|
-
- `errorFn` (Function): Called as `(attempt, error)` after each failed attempt.
|
|
91
|
-
- `options.delayMs` (Number): Base delay between retries in ms. `0` disables delay.
|
|
92
|
-
- `options.backoffFactor` (Number): Multiplier applied per attempt. `1` keeps delay constant; `2` doubles each retry.
|
|
93
|
-
- **Returns:** The first non-throwing result of `callFn`.
|
|
94
|
-
- **Throws:** The last error after `maxAttempts` failures.
|
|
95
|
-
|
|
96
|
-
## Strings
|
|
97
|
-
|
|
98
|
-
### isValidURL(url)
|
|
99
|
-
|
|
100
|
-
Tests whether the input parses as a valid URL via the `URL` constructor.
|
|
101
|
-
|
|
102
|
-
- **Parameters:**
|
|
103
|
-
- `url` (String): Candidate URL.
|
|
104
|
-
- **Returns:** `Boolean`.
|
|
105
|
-
|
|
106
|
-
### splitTrim(string, separator = null)
|
|
107
|
-
|
|
108
|
-
Splits a string, trims each piece, and drops empty results. Default separator is `\r?\n` (any newline).
|
|
109
|
-
|
|
110
|
-
- **Parameters:**
|
|
111
|
-
- `string` (String): Source text.
|
|
112
|
-
- `separator` (String|RegExp|null): Custom delimiter; `null` falls back to newlines.
|
|
113
|
-
- **Returns:** Array of non-empty trimmed strings.
|
|
114
|
-
|
|
115
|
-
### pascalCase(string)
|
|
116
|
-
|
|
117
|
-
Converts arbitrary text to `PascalCase` (uses lodash internally).
|
|
118
|
-
|
|
119
|
-
- **Parameters:**
|
|
120
|
-
- `string` (String): Input text.
|
|
121
|
-
- **Returns:** PascalCase string.
|
|
122
|
-
|
|
123
|
-
### titleCase(string, separator = " ")
|
|
124
|
-
|
|
125
|
-
Capitalizes the first letter of each word delimited by `separator`. Other characters are preserved as-is.
|
|
126
|
-
|
|
127
|
-
- **Parameters:**
|
|
128
|
-
- `string` (String): Input text.
|
|
129
|
-
- `separator` (String): Word boundary. Defaults to a single space.
|
|
130
|
-
- **Returns:** Title-cased string.
|
|
131
|
-
|
|
132
|
-
### limitString(string, limit = 35, omission = "...")
|
|
133
|
-
|
|
134
|
-
Truncates a string if it exceeds `limit` characters and appends `omission`. Strings shorter than the limit are returned unchanged.
|
|
135
|
-
|
|
136
|
-
- **Parameters:**
|
|
137
|
-
- `string` (String): Input text.
|
|
138
|
-
- `limit` (Number): Maximum length of the result (including `omission`).
|
|
139
|
-
- `omission` (String): Suffix used when truncation happens.
|
|
140
|
-
- **Returns:** Possibly truncated string.
|
|
141
|
-
|
|
142
|
-
### safeString(string)
|
|
143
|
-
|
|
144
|
-
Strips HTML tags via the `xss` library and additionally removes the body of dangerous block tags (`<script>`, `<style>`, `<iframe>`, `<object>`, `<embed>`, `<form>`) so leftover CSS/markup cannot leak as text. CSS attribute sanitization is also disabled (no `<style>` attribute support). Intended for rendering untrusted text safely; not a substitute for a full HTML sanitizer like DOMPurify.
|
|
145
|
-
|
|
146
|
-
- **Parameters:**
|
|
147
|
-
- `string` (String): Untrusted text.
|
|
148
|
-
- **Returns:** Sanitized string with no allowed tags.
|
|
149
|
-
|
|
150
|
-
### shuffleString(string)
|
|
151
|
-
|
|
152
|
-
Randomly reorders the characters in a string using lodash's `shuffle` (Fisher-Yates).
|
|
153
|
-
|
|
154
|
-
- **Parameters:**
|
|
155
|
-
- `string` (String): Source string.
|
|
156
|
-
- **Returns:** Shuffled string of the same length.
|
|
157
|
-
|
|
158
|
-
## Random (non-cryptographic, Math.random)
|
|
159
|
-
|
|
160
|
-
### randomBoolean()
|
|
161
|
-
|
|
162
|
-
Returns `true` or `false` with roughly equal probability.
|
|
163
|
-
|
|
164
|
-
- **Returns:** `Boolean`.
|
|
165
|
-
|
|
166
|
-
### randomString(length, useNumbers = true, useUppercase = false)
|
|
167
|
-
|
|
168
|
-
Generates a non-secure random string from configurable character sets.
|
|
169
|
-
|
|
170
|
-
- **Parameters:**
|
|
171
|
-
- `length` (Number): Output length.
|
|
172
|
-
- `useNumbers` (Boolean): Include digits `0-9`.
|
|
173
|
-
- `useUppercase` (Boolean): Include uppercase letters.
|
|
174
|
-
- **Returns:** Random string.
|
|
175
|
-
|
|
176
|
-
### randomHex(length)
|
|
177
|
-
|
|
178
|
-
Generates a non-secure random hexadecimal string.
|
|
179
|
-
|
|
180
|
-
- **Parameters:**
|
|
181
|
-
- `length` (Number): Output length.
|
|
182
|
-
- **Returns:** Hex string of the requested length.
|
|
183
|
-
|
|
184
|
-
### randomInteger(min, max)
|
|
185
|
-
|
|
186
|
-
Returns an integer in `[min, max)`. If called with a single argument it is treated as `max` with `min = 0` (e.g., `randomInteger(10)` returns `0..9`).
|
|
187
|
-
|
|
188
|
-
- **Parameters:**
|
|
189
|
-
- `min` (Number): Inclusive lower bound, or `max` when called with one argument.
|
|
190
|
-
- `max` (Number): Exclusive upper bound.
|
|
191
|
-
- **Returns:** Integer in `[min, max)`.
|
|
192
|
-
- **Throws:** When inputs are not numbers, or when `max <= min`.
|
|
193
|
-
|
|
194
|
-
### randomUuid(useDashes = true)
|
|
195
|
-
|
|
196
|
-
Generates a non-secure UUID v4-shaped string. Sufficient for client-side keys; do not use for security tokens.
|
|
197
|
-
|
|
198
|
-
- **Parameters:**
|
|
199
|
-
- `useDashes` (Boolean): When `false`, dashes are stripped.
|
|
200
|
-
- **Returns:** UUID string.
|
|
201
|
-
|
|
202
|
-
### randomWeighted(object)
|
|
203
|
-
|
|
204
|
-
Picks a key from `object` with probability proportional to its numeric value.
|
|
205
|
-
|
|
206
|
-
- **Parameters:**
|
|
207
|
-
- `object` (Object): Map of key → positive weight.
|
|
208
|
-
- **Returns:** Selected key.
|
|
209
|
-
|
|
210
|
-
### randomElement(object)
|
|
211
|
-
|
|
212
|
-
Picks a random value from an array or from an object's own enumerable values. Returns `undefined` for empty or nullish input.
|
|
213
|
-
|
|
214
|
-
- **Parameters:**
|
|
215
|
-
- `object` (Array|Object): Source collection.
|
|
216
|
-
- **Returns:** A random value, or `undefined`.
|
|
217
|
-
|
|
218
|
-
## Deterministic Random (seeded)
|
|
219
|
-
|
|
220
|
-
### mulberry32(seed)
|
|
221
|
-
|
|
222
|
-
Returns a deterministic PRNG (Mulberry32) seeded by a 32-bit integer or by a string (hashed internally to 32 bits). Each call to the returned function produces a `[0, 1)` float. Very fast, but only 32 bits of state — not for cryptographic use.
|
|
223
|
-
|
|
224
|
-
- **Parameters:**
|
|
225
|
-
- `seed` (Number|String): Seed value.
|
|
226
|
-
- **Returns:** Function that returns a `Number` in `[0, 1)` per call.
|
|
227
|
-
|
|
228
|
-
### seedHex(seed, length)
|
|
229
|
-
|
|
230
|
-
Builds a deterministic hex string of the requested length from a seed via `mulberry32`. Same seed always yields the same output. Useful for short, repeatable identifiers (e.g., proxy session stickiness).
|
|
231
|
-
|
|
232
|
-
- **Parameters:**
|
|
233
|
-
- `seed` (Any): Seed value (coerced to string).
|
|
234
|
-
- `length` (Number): Output length in hex characters. Required.
|
|
235
|
-
- **Returns:** Hex string.
|
|
236
|
-
|
|
237
|
-
## Predicates
|
|
238
|
-
|
|
239
|
-
### checkEmpty(value)
|
|
240
|
-
|
|
241
|
-
Like lodash's `isEmpty` but additionally treats `0` (numeric zero) as empty.
|
|
242
|
-
|
|
243
|
-
- **Parameters:**
|
|
244
|
-
- `value` (Any): Value to test.
|
|
245
|
-
- **Returns:** `Boolean`.
|
|
246
|
-
|
|
247
|
-
### isInt32(value)
|
|
248
|
-
|
|
249
|
-
Tests whether `value` is an integer within the signed 32-bit range.
|
|
250
|
-
|
|
251
|
-
- **Parameters:**
|
|
252
|
-
- `value` (Any): Value to test.
|
|
253
|
-
- **Returns:** `Boolean`.
|
|
254
|
-
|
|
255
|
-
### isPositiveNumber(value)
|
|
256
|
-
|
|
257
|
-
Tests whether `value` is a finite positive number (excludes `NaN`, `Infinity`, non-numbers, `0`, and negatives).
|
|
258
|
-
|
|
259
|
-
- **Parameters:**
|
|
260
|
-
- `value` (Any): Value to test.
|
|
261
|
-
- **Returns:** `Boolean`.
|
|
262
|
-
|
|
263
|
-
## Objects
|
|
264
|
-
|
|
265
|
-
### coerceObjectNumbers(object)
|
|
266
|
-
|
|
267
|
-
Walks an object's own enumerable keys and converts string values that match a numeric pattern (e.g., `"1.5"`, `"-3"`, `"1e3"`) to `Number`. Non-string values and non-strict numeric strings (e.g., `"12abc"`, `"1,000"`) are left untouched. Mutates the input.
|
|
268
|
-
|
|
269
|
-
- **Parameters:**
|
|
270
|
-
- `object` (Object): Object to coerce in place.
|
|
271
|
-
- **Returns:** The same `object`.
|
|
272
|
-
|
|
273
|
-
### coerceObjectIntegers(object)
|
|
274
|
-
|
|
275
|
-
Same as `coerceObjectNumbers` but only converts whole integer strings via `parseInt` (e.g., `"002"` → `2`, `"-7"` → `-7`). Mutates the input.
|
|
276
|
-
|
|
277
|
-
- **Parameters:**
|
|
278
|
-
- `object` (Object): Object to coerce in place.
|
|
279
|
-
- **Returns:** The same `object`.
|
|
280
|
-
|
|
281
|
-
### findNodeByKey(key, node, pair = null)
|
|
282
|
-
|
|
283
|
-
Depth-first search through a nested object for the first node that owns `key`. If `pair` is provided, the node's value at `key` must equal `pair` (strict equality, supports falsy values like `false` / `0` / `""`).
|
|
284
|
-
|
|
285
|
-
- **Parameters:**
|
|
286
|
-
- `key` (String): Property name to find.
|
|
287
|
-
- `node` (Object): Tree to search.
|
|
288
|
-
- `pair` (Any): Optional value constraint. `null` means "match any value".
|
|
289
|
-
- **Returns:** The matching node, or `null` if not found.
|
|
290
|
-
|
|
291
|
-
### waitForProperty(object, property, timeout = 5000, interval = 100)
|
|
292
|
-
|
|
293
|
-
Polls `object` until it owns `property`, then resolves with the property's value. Rejects after `timeout` milliseconds.
|
|
294
|
-
|
|
295
|
-
- **Parameters:**
|
|
296
|
-
- `object` (Object): Object to watch.
|
|
297
|
-
- `property` (String): Property name to wait for.
|
|
298
|
-
- `timeout` (Number): Maximum wait time in milliseconds.
|
|
299
|
-
- `interval` (Number): Poll interval in milliseconds.
|
|
300
|
-
- **Returns:** `Promise` resolving to the property's value.
|
|
301
|
-
- **Throws:** When the property does not appear within `timeout`.
|
|
302
|
-
|
|
303
|
-
### shuffleObject(object)
|
|
304
|
-
|
|
305
|
-
Returns a new object whose entries are in random order (the underlying iteration order is the only thing being shuffled).
|
|
306
|
-
|
|
307
|
-
- **Parameters:**
|
|
308
|
-
- `object` (Object): Source object.
|
|
309
|
-
- **Returns:** New object with the same keys/values in shuffled order.
|
|
310
|
-
|
|
311
|
-
### objectStringify(object)
|
|
312
|
-
|
|
313
|
-
Recursively walks an object and converts every leaf value to `String(value)`. Nested objects and arrays are descended into; mutates the input.
|
|
314
|
-
|
|
315
|
-
- **Parameters:**
|
|
316
|
-
- `object` (Object): Object to mutate.
|
|
317
|
-
- **Returns:** The same `object`.
|
|
318
|
-
|
|
319
|
-
## Cookies
|
|
320
|
-
|
|
321
|
-
### cookiesFromResponse(response, decodeValues = false)
|
|
322
|
-
|
|
323
|
-
Parses the `Set-Cookie` headers off a response-like object (compatible with Node `http`, fetch responses, or anything with `headers["set-cookie"]`) and returns a flat `{name: value}` map via `set-cookie-parser`.
|
|
324
|
-
|
|
325
|
-
- **Parameters:**
|
|
326
|
-
- `response` (Object): Response-like with parsed headers.
|
|
327
|
-
- `decodeValues` (Boolean): Whether to URL-decode cookie values.
|
|
328
|
-
- **Returns:** Map of cookie name → value.
|
|
329
|
-
|
|
330
|
-
### cookiesToHeader(cookies)
|
|
331
|
-
|
|
332
|
-
Serializes a `{name: value}` map into a `Cookie:` header string (`name=value` pairs joined with `"; "`). Null/undefined values are dropped.
|
|
333
|
-
|
|
334
|
-
- **Parameters:**
|
|
335
|
-
- `cookies` (Object): Map of cookie name → value.
|
|
336
|
-
- **Returns:** Cookie header string, or `""` for empty/missing input.
|
|
337
|
-
|
|
338
|
-
### cookiesFromHeader(header)
|
|
339
|
-
|
|
340
|
-
Parses a single `Cookie:` header string into a `{name: value}` map. Pieces without `=` are skipped; multiple `=` inside a value are preserved.
|
|
341
|
-
|
|
342
|
-
- **Parameters:**
|
|
343
|
-
- `header` (String): Cookie header value.
|
|
344
|
-
- **Returns:** Map of cookie name → value. Empty input returns `{}`.
|
|
345
|
-
|
|
346
|
-
## HTTP Helpers
|
|
347
|
-
|
|
348
|
-
### isTransientHttpCode(httpCode)
|
|
349
|
-
|
|
350
|
-
Flags HTTP status codes that are typically transient or worth retrying (missing/`NaN`, `100`, `402`, `407`, `460-469`, anything `≥ 500`).
|
|
351
|
-
|
|
352
|
-
- **Parameters:**
|
|
353
|
-
- `httpCode` (Number|null|undefined): Status code to inspect.
|
|
354
|
-
- **Returns:** `Boolean`.
|
|
355
|
-
|
|
356
|
-
### getResponseError(error, limit = 200)
|
|
357
|
-
|
|
358
|
-
Extracts a short error description from an HTTP error-like object. Prefers `error.response.status|error.response.data`, then `error.response.data`, then `error.message`. Truncates the result to `limit` characters via `limitString`.
|
|
359
|
-
|
|
360
|
-
- **Parameters:**
|
|
361
|
-
- `error` (Error): Error from an HTTP client.
|
|
362
|
-
- `limit` (Number): Maximum length of the returned string.
|
|
363
|
-
- **Returns:** Trimmed error description.
|