@azure-net/node-kit 0.0.0-stage → 0.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 +11 -0
- package/LICENSE +21 -0
- package/README.md +301 -2
- package/VALIDATION.md +866 -0
- package/dist/delivery/http/ResponseBuilder.d.ts +44 -0
- package/dist/delivery/http/ResponseBuilder.js +135 -0
- package/dist/delivery/http/index.d.ts +1 -0
- package/dist/delivery/http/index.js +1 -0
- package/dist/delivery/index.d.ts +1 -0
- package/dist/delivery/index.js +1 -0
- package/dist/domain/Ensure.d.ts +2 -0
- package/dist/domain/Ensure.js +7 -0
- package/dist/domain/EnsureError.d.ts +4 -0
- package/dist/domain/EnsureError.js +7 -0
- package/dist/domain/index.d.ts +2 -0
- package/dist/domain/index.js +2 -0
- package/dist/infrastructure/background/BackgroundTask.d.ts +12 -0
- package/dist/infrastructure/background/BackgroundTask.js +41 -0
- package/dist/infrastructure/background/index.d.ts +1 -0
- package/dist/infrastructure/background/index.js +1 -0
- package/dist/infrastructure/environment/Env.d.ts +19 -0
- package/dist/infrastructure/environment/Env.js +33 -0
- package/dist/infrastructure/environment/index.d.ts +1 -0
- package/dist/infrastructure/environment/index.js +1 -0
- package/dist/infrastructure/index.d.ts +2 -0
- package/dist/infrastructure/index.js +2 -0
- package/dist/tools/DateHelper.d.ts +13 -0
- package/dist/tools/DateHelper.js +77 -0
- package/dist/tools/DurationHelper.d.ts +6 -0
- package/dist/tools/DurationHelper.js +31 -0
- package/dist/tools/EnumHelper.d.ts +26 -0
- package/dist/tools/EnumHelper.js +40 -0
- package/dist/tools/ErrorHelper.d.ts +3 -0
- package/dist/tools/ErrorHelper.js +31 -0
- package/dist/tools/NumberHelper.d.ts +19 -0
- package/dist/tools/NumberHelper.js +49 -0
- package/dist/tools/ObjectHelper.d.ts +28 -0
- package/dist/tools/ObjectHelper.js +90 -0
- package/dist/tools/PromiseHelper.d.ts +3 -0
- package/dist/tools/PromiseHelper.js +7 -0
- package/dist/tools/StringHelper.d.ts +75 -0
- package/dist/tools/StringHelper.js +127 -0
- package/dist/tools/UidHelper.d.ts +56 -0
- package/dist/tools/UidHelper.js +132 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.js +9 -0
- package/dist/validation/Engine.d.ts +6 -0
- package/dist/validation/Engine.js +406 -0
- package/dist/validation/Result.d.ts +31 -0
- package/dist/validation/Result.js +264 -0
- package/dist/validation/Symbols.d.ts +1 -0
- package/dist/validation/Symbols.js +1 -0
- package/dist/validation/Types.d.ts +105 -0
- package/dist/validation/Types.js +1 -0
- package/dist/validation/Validator.d.ts +26 -0
- package/dist/validation/Validator.js +95 -0
- package/dist/validation/index.d.ts +5 -0
- package/dist/validation/index.js +5 -0
- package/dist/validation/messages/En.d.ts +3 -0
- package/dist/validation/messages/En.js +118 -0
- package/dist/validation/messages/Ru.d.ts +3 -0
- package/dist/validation/messages/Ru.js +118 -0
- package/dist/validation/messages/index.d.ts +7 -0
- package/dist/validation/messages/index.js +7 -0
- package/dist/validation/rules/CustomRules.d.ts +8 -0
- package/dist/validation/rules/CustomRules.js +64 -0
- package/dist/validation/rules/Factory.d.ts +4 -0
- package/dist/validation/rules/Factory.js +21 -0
- package/dist/validation/rules/StringRules.d.ts +105 -0
- package/dist/validation/rules/StringRules.js +481 -0
- package/dist/validation/rules/StructureRules.d.ts +30 -0
- package/dist/validation/rules/StructureRules.js +145 -0
- package/dist/validation/rules/ValueRules.d.ts +82 -0
- package/dist/validation/rules/ValueRules.js +365 -0
- package/dist/validation/rules/index.d.ts +151 -0
- package/dist/validation/rules/index.js +19 -0
- package/package.json +77 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
- Initial framework-independent Node.js toolkit; no runtime dependencies.
|
|
6
|
+
- Typed helpers for enums, IDs, strings, numbers, dates, durations, objects, promises and errors.
|
|
7
|
+
- Domain invariants through `Ensure` and `EnsureError`.
|
|
8
|
+
- Explicit-source environment parsing with `Env`.
|
|
9
|
+
- Typed HTTP envelopes with `ResponseBuilder` and background I/O tracking with `BackgroundTask`.
|
|
10
|
+
- Array-based validation with sync/async execution, nested error trees, custom rules and English/Russian catalogs.
|
|
11
|
+
- Explicit subpath imports only; no catch-all package-root entry point.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Pixel1917
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,302 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @azure-net/node-kit
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Framework-independent Node.js toolkit for the Azure-net ecosystem.
|
|
4
|
+
|
|
5
|
+
It has no runtime dependencies and does not depend on Svelte, Hono, or Bun APIs.
|
|
6
|
+
The helpers use standard JavaScript and built-in `node:crypto` / `node:util` APIs.
|
|
7
|
+
|
|
8
|
+
## Installation and imports
|
|
9
|
+
|
|
10
|
+
Requires Node.js 22 or newer. The test suite also runs on Bun.
|
|
11
|
+
|
|
12
|
+
```sh
|
|
13
|
+
npm install @azure-net/node-kit
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
There is no package-root import. Choose the entry point by responsibility:
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { StringHelper, EnumHelper } from "@azure-net/node-kit/tools";
|
|
20
|
+
import { Ensure, EnsureError } from "@azure-net/node-kit/domain";
|
|
21
|
+
import { ResponseBuilder } from "@azure-net/node-kit/delivery";
|
|
22
|
+
import { Env, BackgroundTask } from "@azure-net/node-kit/infrastructure";
|
|
23
|
+
import { Validator, rules } from "@azure-net/node-kit/validation";
|
|
24
|
+
import { validationMessagesRu } from "@azure-net/node-kit/validation/messages/ru";
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Focused entry points are also available: `/delivery/http`,
|
|
28
|
+
`/infrastructure/environment`, `/infrastructure/background`,
|
|
29
|
+
`/validation/messages` and `/validation/messages/en`.
|
|
30
|
+
Imports from the package root or private `dist` paths are deliberately blocked.
|
|
31
|
+
|
|
32
|
+
## Development
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
pnpm install
|
|
36
|
+
pnpm build
|
|
37
|
+
pnpm check
|
|
38
|
+
pnpm test
|
|
39
|
+
pnpm test:bun
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The build emits ESM JavaScript and TypeScript declarations into `dist`.
|
|
43
|
+
Each build first removes stale generated output. Run `pnpm release:check` for
|
|
44
|
+
a clean build, type checks and Node tests. `npm pack` runs that same gate through
|
|
45
|
+
`prepack`; run `pnpm test:bun` separately to verify Bun before publishing.
|
|
46
|
+
|
|
47
|
+
## Validation
|
|
48
|
+
|
|
49
|
+
See the [Validation API reference](VALIDATION.md) for typed rule arrays, sync/async
|
|
50
|
+
validators, localization, nested errors, the full rule catalog, and HTTP integration.
|
|
51
|
+
|
|
52
|
+
## Domain assertions
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { Ensure, EnsureError } from "@azure-net/node-kit/domain";
|
|
56
|
+
import { StringHelper } from "@azure-net/node-kit/tools";
|
|
57
|
+
|
|
58
|
+
Ensure(
|
|
59
|
+
StringHelper.isValid(name, { maxLength: 255 }),
|
|
60
|
+
"Name must contain 1 to 255 characters.",
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
Ensure(version >= 1, () => new RangeError("Version must be positive."));
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`Ensure(condition, error)` returns nothing on success. A failed condition with a
|
|
67
|
+
string throws `EnsureError` with that exact message. A synchronous error factory
|
|
68
|
+
runs only on failure and its Error is thrown unchanged. Exceptions raised by the
|
|
69
|
+
factory also propagate unchanged. The assertion signature supports TypeScript
|
|
70
|
+
narrowing when the condition is a type guard. No inheritance, HTTP status mapping,
|
|
71
|
+
localization or shared state is involved. Both exports are available from
|
|
72
|
+
`@azure-net/node-kit/domain`. `EnsureError` supports the standard `ErrorOptions.cause` option.
|
|
73
|
+
|
|
74
|
+
## Environment
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { Env } from "@azure-net/node-kit/infrastructure/environment";
|
|
78
|
+
import {
|
|
79
|
+
DurationHelper,
|
|
80
|
+
NumberHelper,
|
|
81
|
+
StringHelper,
|
|
82
|
+
} from "@azure-net/node-kit/tools";
|
|
83
|
+
|
|
84
|
+
const env = new Env(process.env); // Or new Env({ PORT: '3000', DB_PASSWORD: '' }).
|
|
85
|
+
|
|
86
|
+
const password = env.get("DB_PASSWORD", { allowEmpty: true });
|
|
87
|
+
const port = env.get("PORT", {
|
|
88
|
+
fallback: "3000",
|
|
89
|
+
parse: ({ value, invalid }) => {
|
|
90
|
+
if (!StringHelper.matches(value, /^\d+$/))
|
|
91
|
+
return invalid("must contain only digits.");
|
|
92
|
+
const number = Number(value);
|
|
93
|
+
return NumberHelper.isValid(number, {
|
|
94
|
+
min: 1,
|
|
95
|
+
max: 65535,
|
|
96
|
+
safeInteger: true,
|
|
97
|
+
})
|
|
98
|
+
? number
|
|
99
|
+
: invalid("must be an integer between 1 and 65535.");
|
|
100
|
+
},
|
|
101
|
+
}); // number
|
|
102
|
+
|
|
103
|
+
const timeout = env.get("REQUEST_TIMEOUT", {
|
|
104
|
+
fallback: "30s",
|
|
105
|
+
parse: ({ value, invalid }) =>
|
|
106
|
+
DurationHelper.toSeconds(value) ?? invalid("must be a valid duration."),
|
|
107
|
+
}); // number
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`Env` is also exported from `@azure-net/node-kit/infrastructure`.
|
|
111
|
+
The constructor requires an explicit object of `string | undefined` values. It holds
|
|
112
|
+
that object by reference and reads its own properties on each call, without copying,
|
|
113
|
+
caching, mutating it or falling back to global environment variables. Pass a copied
|
|
114
|
+
object if a configuration snapshot is desired. There is no `.env` file loader.
|
|
115
|
+
|
|
116
|
+
`get` returns a string, or the inferred result of `parse`. Missing values throw an
|
|
117
|
+
Error unless a string fallback is provided. The fallback applies only to `undefined`
|
|
118
|
+
and passes the same validation and parsing. Empty and whitespace-only strings throw
|
|
119
|
+
unless `allowEmpty: true`; that option does not allow missing values. Values are never
|
|
120
|
+
trimmed or coerced automatically. Non-string runtime values throw even with a fallback.
|
|
121
|
+
|
|
122
|
+
`invalid(message)` throws an Error with the variable name and your message, and has
|
|
123
|
+
return type `never`. Values are not automatically included in errors; do not embed
|
|
124
|
+
secrets in custom messages. Unexpected exceptions from `parse` propagate unchanged.
|
|
125
|
+
No process-global configuration or request state is created.
|
|
126
|
+
|
|
127
|
+
## Tools
|
|
128
|
+
|
|
129
|
+
### Durations
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { DurationHelper } from "@azure-net/node-kit/tools";
|
|
133
|
+
|
|
134
|
+
DurationHelper.toSeconds(process.env.REQUEST_TIMEOUT ?? "30s"); // number | null
|
|
135
|
+
DurationHelper.toMilliseconds("1.5m"); // 90000
|
|
136
|
+
DurationHelper.toSeconds("500ms"); // 0.5
|
|
137
|
+
DurationHelper.toSeconds("invalid"); // null
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Accepts decimal strings with `ms`, `s`, `m`, `h`, or no unit (seconds).
|
|
141
|
+
Whitespace, signs, exponent notation and numeric inputs are rejected without coercion.
|
|
142
|
+
Values must resolve to whole milliseconds between 0 and 2147483647 inclusive.
|
|
143
|
+
Decimal arithmetic is exact: submillisecond values return `null`, not a rounded result.
|
|
144
|
+
Zero is valid. This parser does not read environment variables, apply defaults or throw
|
|
145
|
+
validation errors. Defaults apply only to absent input at the caller, not malformed input.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import {
|
|
149
|
+
EnumHelper,
|
|
150
|
+
StringHelper,
|
|
151
|
+
NumberHelper,
|
|
152
|
+
DateHelper,
|
|
153
|
+
UidHelper,
|
|
154
|
+
ObjectHelper,
|
|
155
|
+
PromiseHelper,
|
|
156
|
+
ErrorHelper,
|
|
157
|
+
} from "@azure-net/node-kit/tools";
|
|
158
|
+
|
|
159
|
+
enum Status {
|
|
160
|
+
Created = 0,
|
|
161
|
+
Active = 1,
|
|
162
|
+
}
|
|
163
|
+
const input: unknown = 1;
|
|
164
|
+
EnumHelper.isInEnum(Status, input); // boolean; narrows input to Status
|
|
165
|
+
EnumHelper.getValue(Status, input); // Status | null
|
|
166
|
+
EnumHelper.getValue(Status, "1"); // null, no coercion or name lookup
|
|
167
|
+
|
|
168
|
+
StringHelper.isValid("Name", { maxLength: 255 });
|
|
169
|
+
StringHelper.isValid(null, {
|
|
170
|
+
allowNull: true,
|
|
171
|
+
allowEmpty: true,
|
|
172
|
+
maxLength: 1000,
|
|
173
|
+
});
|
|
174
|
+
StringHelper.isValid(process.env.DB_PASSWORD, { allowEmpty: true });
|
|
175
|
+
StringHelper.isValid("abc", {
|
|
176
|
+
minLength: 1,
|
|
177
|
+
maxLength: 20,
|
|
178
|
+
pattern: /^[a-z]+$/,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
NumberHelper.isPositiveInteger(1);
|
|
182
|
+
NumberHelper.isValid(25, { min: 1, max: 100, safeInteger: true });
|
|
183
|
+
DateHelper.isBefore("2026-01-01", "2026-02-01");
|
|
184
|
+
UidHelper.isValidUuid(UidHelper.generateUuid());
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
- `EnumHelper` also retains `associateByIndex`, `associateByKey` and `toEntries`.
|
|
188
|
+
Numeric reverse mappings are excluded. Normal runtime enums and `as const`
|
|
189
|
+
objects are supported; erased `const enum` declarations are not runtime objects.
|
|
190
|
+
- `StringHelper.isValid` requires a nonblank string by default. `allowNull`,
|
|
191
|
+
`allowUndefined` and `allowEmpty` are independent. Length uses UTF-16 code units,
|
|
192
|
+
including surrounding whitespace unless `trim: true` is set. No input is mutated.
|
|
193
|
+
`matches` does not mutate a regex's `lastIndex`. Predicates return booleans, not errors.
|
|
194
|
+
Existing `pluralize`, `truncate`, `capitalize`, `decapitalize` and
|
|
195
|
+
`isEmptyOrWhitespace` methods are retained.
|
|
196
|
+
- `NumberHelper` never coerces values: `''`, `'1'`, `false`, `NaN` and infinities
|
|
197
|
+
are not valid finite numbers. Integer and safe-integer checks are distinct.
|
|
198
|
+
- `DateHelper` accepts valid Dates, integer millisecond timestamps and strict ISO
|
|
199
|
+
strings. Date-only and zone-less strings use UTC. Fractions are truncated to
|
|
200
|
+
milliseconds. Invalid dates return `null` from `compare` and `false` from predicates.
|
|
201
|
+
`isSame` compares instants, not calendar days. `isBetween` accepts reversed bounds;
|
|
202
|
+
its fourth argument controls inclusivity. There are no locale settings or formatters.
|
|
203
|
+
- `UidHelper` uses cryptographic randomness with no fallback. `isValidUuid` checks
|
|
204
|
+
UUID v4. `generateHashId` produces a random hex string, not a cryptographic digest.
|
|
205
|
+
Timestamp IDs without randomness are only process-local; no random ID method
|
|
206
|
+
guarantees uniqueness. Invalid generation parameters throw `RangeError`.
|
|
207
|
+
- `ObjectHelper.deepClone` uses native `structuredClone`: cycles, Dates, Maps, Sets,
|
|
208
|
+
BigInt and typed arrays are supported. Functions cannot be cloned. Custom class
|
|
209
|
+
prototypes, property descriptors and symbol properties are not preserved; Buffer
|
|
210
|
+
is not guaranteed to remain Buffer. It is a data clone, not an instance clone.
|
|
211
|
+
`clone` is shallow and accepts arrays/plain objects only, preserving descriptors.
|
|
212
|
+
`equals` uses `isDeepStrictEqual`; `compareAsString` remains explicitly JSON-only.
|
|
213
|
+
`pick`/`omit` use own properties, support symbols and safely handle `__proto__`.
|
|
214
|
+
`isObjectEmpty` rejects nonobjects and noncollection instances; Map/Set use size.
|
|
215
|
+
`isAllKeysEmpty` treats nullish leaves and empty containers as empty, supports
|
|
216
|
+
cycles and deep nesting, and does not invoke getters (accessors count as nonempty).
|
|
217
|
+
- `PromiseHelper.isPromiseLike` checks for callable `then` (including custom thenables).
|
|
218
|
+
- `ErrorHelper.toError` retains existing Error instances and attaches other input as `cause`.
|
|
219
|
+
|
|
220
|
+
The test suite exercises runtime behavior on Node and Bun, plus compile-time enum
|
|
221
|
+
inference and narrowing. The package does not install process-global request state,
|
|
222
|
+
timers or event listeners.
|
|
223
|
+
|
|
224
|
+
## HTTP responses
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
import { ResponseBuilder } from "@azure-net/node-kit/delivery";
|
|
228
|
+
|
|
229
|
+
const response = ResponseBuilder.Created(
|
|
230
|
+
{ id: "1" },
|
|
231
|
+
{
|
|
232
|
+
message: "User created",
|
|
233
|
+
meta: { requestId: "request-1" },
|
|
234
|
+
},
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
// Hono route: toJSON returns an object, not an encoded string.
|
|
238
|
+
return c.json(response.toJSON(), response.status);
|
|
239
|
+
// Alternatively, return response.toResponse() for a native Fetch Response.
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Every response contains `success`, `status`, `message`, `data`, `meta`, `errors`
|
|
243
|
+
and `code`. Omitted payload fields become `null`. Data, metadata and errors are
|
|
244
|
+
inferred independently. Presets retain literal status types for Hono's `c.json`
|
|
245
|
+
and RPC inference. The package has no Hono dependency.
|
|
246
|
+
|
|
247
|
+
`new ResponseBuilder(data, options)` defaults to status 200. Presets:
|
|
248
|
+
`Ok` (200), `Created` (201), `Accepted` (202), `BadRequest` (400),
|
|
249
|
+
`Unauthorized` (401), `Forbidden` (403), `NotFound` (404), `Conflict` (409),
|
|
250
|
+
`UnprocessableEntity` (422), `TooManyRequests` (429), `InternalServerError` (500).
|
|
251
|
+
All accept optional data first and options second. Presets fix the status;
|
|
252
|
+
options can customize `message`, `meta`, `errors` and `code`. Error presets
|
|
253
|
+
provide an uppercase snake-case code; pass `code: null` to omit its value.
|
|
254
|
+
Defaults are English HTTP messages; supply your own localized message as needed.
|
|
255
|
+
|
|
256
|
+
`success` is computed as `status < 400`. Non-null `errors` and `code` on a
|
|
257
|
+
successful response are rejected. Statuses 204, 205, 304 and informational
|
|
258
|
+
statuses are rejected because this builder always emits an envelope with a body.
|
|
259
|
+
The envelope is shallowly frozen; user data is not frozen or copied.
|
|
260
|
+
|
|
261
|
+
`toResponse(headers?)` sets the HTTP status and uses native `Response.json`.
|
|
262
|
+
Payloads must be JSON-serializable: BigInt and circular references throw during
|
|
263
|
+
serialization. Classes follow standard JSON serialization (for example, Dates
|
|
264
|
+
become strings). Do not pass `JSON.stringify(...)` into Hono's `c.json`.
|
|
265
|
+
|
|
266
|
+
## Background I/O tasks
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { BackgroundTask } from "@azure-net/node-kit/infrastructure";
|
|
270
|
+
|
|
271
|
+
const background = new BackgroundTask({
|
|
272
|
+
onError: async (error) => {
|
|
273
|
+
console.error("Background operation failed", error);
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
background.run(() => emailService.sendWelcome(email));
|
|
278
|
+
console.log(background.pendingCount);
|
|
279
|
+
|
|
280
|
+
// After stopping incoming requests, before closing DB and other dependencies:
|
|
281
|
+
await background.drain();
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
`run` accepts a task factory and returns `void` immediately. Factories start in
|
|
285
|
+
microtasks and run independently without a concurrency limit. Synchronous throws,
|
|
286
|
+
rejected promises and thenables are sent to the required `onError` callback.
|
|
287
|
+
If that callback throws or rejects, the handler failure and original error are
|
|
288
|
+
reported via `console.error`; normal task failures do not reject `drain`.
|
|
289
|
+
|
|
290
|
+
`pendingCount` includes asynchronous error handling. `drain` waits for active
|
|
291
|
+
tasks, their error handlers, and tasks added while it waits. Multiple callers may
|
|
292
|
+
drain concurrently. Completed tasks are removed from the registry. Each manager
|
|
293
|
+
owns its own registry; there are no global listeners, timers or shutdown hooks.
|
|
294
|
+
Do not await `drain` from a task or its error handler: it would wait for itself.
|
|
295
|
+
Stop task producers first if drain is used during shutdown; a never-settling task
|
|
296
|
+
will keep it pending. It neither cancels tasks nor forces the process to stay alive.
|
|
297
|
+
|
|
298
|
+
This is in-process best-effort background I/O, not a durable queue. Process crashes
|
|
299
|
+
can lose tasks. It does not move CPU work to another thread: synchronous work still
|
|
300
|
+
blocks the event loop. Use worker threads for CPU-intensive work and durable queues
|
|
301
|
+
when jobs must survive restarts. Pass captured values rather than keeping entire
|
|
302
|
+
HTTP contexts alive for the duration of a background task.
|