nodecore-kit 0.3.0 → 0.4.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 +530 -42
- package/dist/index.cjs +1534 -206
- package/dist/index.d.ts +919 -36
- package/dist/index.js +1510 -207
- package/package.json +9 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,15 +1,76 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { AxiosProgressEvent } from 'axios';
|
|
2
|
+
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { Schema, ValidationOptions } from 'joi';
|
|
4
|
+
import { RedisOptions } from 'ioredis';
|
|
5
|
+
import { MessageAttributeValue } from '@aws-sdk/client-sqs';
|
|
6
|
+
import { ObjectCannedACL } from '@aws-sdk/client-s3';
|
|
7
|
+
import { Readable } from 'stream';
|
|
2
8
|
import jwt, { SignOptions } from 'jsonwebtoken';
|
|
3
9
|
|
|
4
|
-
|
|
10
|
+
type HttpMethod = "GET" | "DELETE" | "POST" | "PATCH" | "PUT";
|
|
11
|
+
interface RequestOptions<TData = unknown> {
|
|
5
12
|
url: string;
|
|
6
|
-
method?:
|
|
7
|
-
headers?: Record<string,
|
|
13
|
+
method?: HttpMethod;
|
|
14
|
+
headers?: Record<string, string>;
|
|
15
|
+
/** Auth token — will be sent as `Bearer <token>` */
|
|
8
16
|
token?: string;
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
/** Request body — plain object, array, or FormData */
|
|
18
|
+
data?: TData;
|
|
19
|
+
/** Query string parameters */
|
|
20
|
+
params?: Record<string, any>;
|
|
21
|
+
/** Request timeout in milliseconds (default: 10_000) */
|
|
22
|
+
timeout?: number;
|
|
23
|
+
/** Number of retry attempts on network errors or 5xx responses (default: 0) */
|
|
24
|
+
retries?: number;
|
|
25
|
+
/** Upload/download progress callback */
|
|
26
|
+
onProgress?: (event: AxiosProgressEvent) => void;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Makes an HTTP request via Axios with consistent error handling,
|
|
30
|
+
* optional auth, query params, timeout, and retry support.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* const user = await makeRequest<User>({ url: "/api/user/1" });
|
|
34
|
+
* const post = await makeRequest<Post>({ url: "/api/posts", method: "POST", data: { title: "Hello" } });
|
|
35
|
+
*/
|
|
36
|
+
declare const makeRequest: <TResponse = Record<string, any>, TData = unknown>(options: RequestOptions<TData>, _retryCount?: number) => Promise<TResponse>;
|
|
11
37
|
|
|
12
|
-
|
|
38
|
+
interface FieldConstraint {
|
|
39
|
+
schema: Schema;
|
|
40
|
+
options?: ValidationOptions;
|
|
41
|
+
}
|
|
42
|
+
interface JoiConstraints {
|
|
43
|
+
body?: FieldConstraint;
|
|
44
|
+
params?: FieldConstraint;
|
|
45
|
+
query?: FieldConstraint;
|
|
46
|
+
headers?: FieldConstraint;
|
|
47
|
+
files?: FieldConstraint;
|
|
48
|
+
}
|
|
49
|
+
interface DirectConstraint {
|
|
50
|
+
schema: Schema;
|
|
51
|
+
data: unknown;
|
|
52
|
+
options?: ValidationOptions;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Express middleware that validates req.body, params, query, headers, and/or files.
|
|
56
|
+
* Replaces each field with the sanitized, validated value from Joi.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* router.post("/users", joiMiddleware({
|
|
60
|
+
* body: { schema: createUserSchema },
|
|
61
|
+
* params: { schema: idParamSchema },
|
|
62
|
+
* }), createUser);
|
|
63
|
+
*/
|
|
64
|
+
declare const joiMiddleware: (constraints: JoiConstraints) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Validates data directly outside of a middleware context.
|
|
67
|
+
* Throws a ValidationError if invalid.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const value = joiValidate({ schema: createUserSchema, data: req.body });
|
|
71
|
+
* const typed = joiValidate<CreateUserDto>({ schema: createUserSchema, data: req.body });
|
|
72
|
+
*/
|
|
73
|
+
declare const joiValidate: <T = unknown>({ schema, data, options, }: DirectConstraint) => T;
|
|
13
74
|
|
|
14
75
|
declare const HTTP_STATUS: {
|
|
15
76
|
readonly OK: {
|
|
@@ -64,6 +125,14 @@ declare const HTTP_STATUS: {
|
|
|
64
125
|
type HttpStatusKey = keyof typeof HTTP_STATUS;
|
|
65
126
|
type HttpStatus = (typeof HTTP_STATUS)[HttpStatusKey];
|
|
66
127
|
declare const HTTP_STATUS_CODE_ERROR: Record<number, string>;
|
|
128
|
+
/**
|
|
129
|
+
* Base class for all application errors.
|
|
130
|
+
* Extends the native Error with an HTTP status code, status message,
|
|
131
|
+
* optional machine-readable error code, and optional metadata.
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* throw new AppError(HTTP_STATUS.NOT_FOUND, "User not found", "NOT_FOUND");
|
|
135
|
+
*/
|
|
67
136
|
declare class AppError extends Error {
|
|
68
137
|
readonly statusCode: number;
|
|
69
138
|
readonly statusMessage: string;
|
|
@@ -71,36 +140,107 @@ declare class AppError extends Error {
|
|
|
71
140
|
readonly meta?: Record<string, any>;
|
|
72
141
|
constructor(status: HttpStatus, message?: string | null, errorCode?: string, meta?: Record<string, any>);
|
|
73
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* 422 — request is well-formed but contains invalid field values.
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* throw new ValidationError("Email is invalid");
|
|
148
|
+
* throw new ValidationError("Validation failed", { fields: ["email", "name"] });
|
|
149
|
+
*/
|
|
74
150
|
declare class ValidationError extends AppError {
|
|
75
151
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
76
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* 401 — caller is not authenticated.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* throw new AuthenticationError("Invalid credentials");
|
|
158
|
+
*/
|
|
77
159
|
declare class AuthenticationError extends AppError {
|
|
78
160
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
79
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* 403 — caller is authenticated but not permitted to access this resource.
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* throw new AuthorizationError("You do not have permission to perform this action");
|
|
167
|
+
*/
|
|
80
168
|
declare class AuthorizationError extends AppError {
|
|
81
169
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
82
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* 404 — requested resource does not exist.
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* throw new NotFoundError("User not found");
|
|
176
|
+
*/
|
|
83
177
|
declare class NotFoundError extends AppError {
|
|
84
178
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
85
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* 498 — token has passed its expiry time.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* throw new TokenExpiredError("Session has expired, please log in again");
|
|
185
|
+
*/
|
|
86
186
|
declare class TokenExpiredError extends AppError {
|
|
87
187
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
88
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* 499 — token is malformed or its signature is invalid.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* throw new TokenInvalidError("Token is invalid");
|
|
194
|
+
*/
|
|
89
195
|
declare class TokenInvalidError extends AppError {
|
|
90
196
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
91
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* 400 — request is malformed or missing required fields.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* throw new BadRequestError("Request body is missing required fields");
|
|
203
|
+
*/
|
|
92
204
|
declare class BadRequestError extends AppError {
|
|
93
205
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
94
206
|
}
|
|
207
|
+
/**
|
|
208
|
+
* 500 — unexpected server-side failure.
|
|
209
|
+
* Use `meta` to attach the original cause without leaking internals to the client.
|
|
210
|
+
*
|
|
211
|
+
* @example
|
|
212
|
+
* throw new ServerError("Failed to connect to database", { cause: err });
|
|
213
|
+
*/
|
|
95
214
|
declare class ServerError extends AppError {
|
|
96
215
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
97
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* 409 — resource already exists.
|
|
219
|
+
*
|
|
220
|
+
* @example
|
|
221
|
+
* throw new ExistingError("A user with this email already exists");
|
|
222
|
+
*/
|
|
98
223
|
declare class ExistingError extends AppError {
|
|
99
224
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
100
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* 204 — operation succeeded but there is no content to return.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* throw new NoContent();
|
|
231
|
+
*/
|
|
101
232
|
declare class NoContent extends AppError {
|
|
102
233
|
constructor(message?: string | null, meta?: Record<string, any>);
|
|
103
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* Framework-agnostic error normalizer.
|
|
237
|
+
* Converts any thrown value into a consistent response shape.
|
|
238
|
+
* Handles AppError, Axios errors, native Error, and plain strings.
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* const response = errorHandler(err, "FATAL_ERROR", "auth-service");
|
|
242
|
+
* res.status(response.httpStatusCode).json(response);
|
|
243
|
+
*/
|
|
104
244
|
declare const errorHandler: (err: any, ERROR_TYPE?: string, service?: string) => {
|
|
105
245
|
message: any;
|
|
106
246
|
error: any;
|
|
@@ -108,6 +248,13 @@ declare const errorHandler: (err: any, ERROR_TYPE?: string, service?: string) =>
|
|
|
108
248
|
success: boolean;
|
|
109
249
|
service: string;
|
|
110
250
|
};
|
|
251
|
+
/**
|
|
252
|
+
* Optional Express error-handling middleware built on top of `errorHandler`.
|
|
253
|
+
* Drop at the end of your middleware chain to catch all unhandled errors.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* app.use(expressErrorMiddleware());
|
|
257
|
+
*/
|
|
111
258
|
declare const expressErrorMiddleware: () => (err: any, req: any, res: any, next: any) => void;
|
|
112
259
|
|
|
113
260
|
declare const paginate: (totalCount: number, currentPage: number, perPage: number) => {
|
|
@@ -117,55 +264,376 @@ declare const paginate: (totalCount: number, currentPage: number, perPage: numbe
|
|
|
117
264
|
declare const formatDate: (date: Date) => string;
|
|
118
265
|
declare const parseJSON: (value: any) => any;
|
|
119
266
|
declare const stringifyJSON: (value: any) => any;
|
|
120
|
-
declare const isObject: (val: any) => boolean;
|
|
121
|
-
declare const sleep: (ms: number) => Promise<unknown>;
|
|
122
|
-
declare const capitalize: (str: string) => string;
|
|
123
|
-
declare const isEmpty: (val: any) => boolean;
|
|
124
267
|
|
|
268
|
+
type UUIDVersion = "v1" | "v4";
|
|
269
|
+
type UUIDBinary = Buffer;
|
|
270
|
+
type UUIDBuffer = Buffer;
|
|
125
271
|
declare const uuid: {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Converts a UUID string to its optimized binary representation (Buffer).
|
|
274
|
+
* Reorders bytes for better index performance in databases like MySQL.
|
|
275
|
+
* If no UUID is provided, generates a new v1 UUID.
|
|
276
|
+
*/
|
|
277
|
+
toBinary: (value?: string | UUIDBuffer) => UUIDBinary;
|
|
278
|
+
/**
|
|
279
|
+
* Converts a binary UUID Buffer back to its string representation.
|
|
280
|
+
*/
|
|
281
|
+
toString: (binary: UUIDBinary | string) => string;
|
|
282
|
+
/**
|
|
283
|
+
* Generates a new UUID string.
|
|
284
|
+
* Defaults to v4 (random). Pass "v1" for time-based UUIDs.
|
|
285
|
+
*
|
|
286
|
+
* @example
|
|
287
|
+
* uuid.get() // v4 UUID
|
|
288
|
+
* uuid.get("v1") // v1 UUID
|
|
289
|
+
*/
|
|
290
|
+
get: (version?: UUIDVersion) => string;
|
|
291
|
+
/**
|
|
292
|
+
* Returns true if the given string is a valid UUID.
|
|
293
|
+
*/
|
|
294
|
+
isValid: (value: string) => boolean;
|
|
295
|
+
/** The nil UUID — all zeros. Useful as a default/placeholder. */
|
|
296
|
+
nil: "00000000-0000-0000-0000-000000000000";
|
|
297
|
+
/**
|
|
298
|
+
* Converts specified keys of an object from binary UUIDs to strings.
|
|
299
|
+
* Returns a shallow copy — does NOT mutate the original.
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* uuid.manyToString({ id: <Buffer>, name: "foo" }, ["id"])
|
|
303
|
+
* // { id: "xxxxxxxx-...", name: "foo" }
|
|
304
|
+
*/
|
|
305
|
+
manyToString: <T extends Record<string, any>>(data: T, keys?: string[]) => T;
|
|
306
|
+
/**
|
|
307
|
+
* Converts specified keys of an object from UUID strings to binary Buffers.
|
|
308
|
+
* Returns a shallow copy — does NOT mutate the original.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* uuid.manyToBinary({ id: "xxxxxxxx-...", name: "foo" }, ["id"])
|
|
312
|
+
* // { id: <Buffer>, name: "foo" }
|
|
313
|
+
*/
|
|
314
|
+
manyToBinary: <T extends Record<string, any>>(data: T, keys?: string[]) => T;
|
|
132
315
|
};
|
|
133
316
|
|
|
317
|
+
/**
|
|
318
|
+
* Pauses execution for the given number of milliseconds.
|
|
319
|
+
*
|
|
320
|
+
* @example
|
|
321
|
+
* await sleep(1000); // waits 1 second
|
|
322
|
+
*/
|
|
323
|
+
declare const sleep: (ms: number) => Promise<void>;
|
|
324
|
+
interface RetryOptions {
|
|
325
|
+
/** Number of retry attempts (default: 3) */
|
|
326
|
+
retries?: number;
|
|
327
|
+
/** Base delay in ms (default: 500) */
|
|
328
|
+
delay?: number;
|
|
329
|
+
/** Use exponential backoff — doubles delay each attempt (default: true) */
|
|
330
|
+
exponential?: boolean;
|
|
331
|
+
/** Called on each failed attempt before retrying */
|
|
332
|
+
onError?: (err: unknown, attempt: number) => void;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Retries an async function on failure with optional exponential backoff.
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* const data = await retry(() => fetchUser(id), { retries: 3, exponential: true });
|
|
339
|
+
*/
|
|
340
|
+
declare const retry: <T>(fn: () => Promise<T>, options?: RetryOptions) => Promise<T>;
|
|
341
|
+
/**
|
|
342
|
+
* Rejects if the given promise doesn't resolve within `ms` milliseconds.
|
|
343
|
+
* Cleans up the internal timer whether the promise resolves or rejects.
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* const data = await timeout(fetchUser(id), 5000);
|
|
347
|
+
*/
|
|
348
|
+
declare const timeout: <T>(promise: Promise<T>, ms: number) => Promise<T>;
|
|
349
|
+
interface DebouncedFn<T extends (...args: any[]) => any> {
|
|
350
|
+
(...args: Parameters<T>): ReturnType<T> | undefined;
|
|
351
|
+
/** Cancels any pending invocation */
|
|
352
|
+
cancel: () => void;
|
|
353
|
+
/** Immediately invokes the pending call if one exists */
|
|
354
|
+
flush: (...args: Parameters<T>) => ReturnType<T> | undefined;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Returns a debounced version of `fn` that delays invocation until
|
|
358
|
+
* `delay`ms have passed since the last call.
|
|
359
|
+
*
|
|
360
|
+
* @example
|
|
361
|
+
* const onSearch = debounce((query: string) => search(query), 300);
|
|
362
|
+
* onSearch.cancel(); // cancel pending call
|
|
363
|
+
* onSearch.flush(); // invoke immediately
|
|
364
|
+
*/
|
|
365
|
+
declare const debounce: <T extends (...args: any[]) => any>(fn: T, delay: number) => DebouncedFn<T>;
|
|
366
|
+
interface ThrottledFn<T extends (...args: any[]) => any> {
|
|
367
|
+
(...args: Parameters<T>): ReturnType<T> | undefined;
|
|
368
|
+
/** Cancels any pending trailing call */
|
|
369
|
+
cancel: () => void;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Returns a throttled version of `fn` that invokes at most once per `limit`ms.
|
|
373
|
+
* Executes on the leading edge and optionally on the trailing edge.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* const onScroll = throttle(() => updatePosition(), 100);
|
|
377
|
+
*/
|
|
378
|
+
declare const throttle: <T extends (...args: any[]) => any>(fn: T, limit: number, { trailing }?: {
|
|
379
|
+
trailing?: boolean;
|
|
380
|
+
}) => ThrottledFn<T>;
|
|
381
|
+
/**
|
|
382
|
+
* Caches the result of `fn` based on its arguments.
|
|
383
|
+
* The cache key is built by JSON-serializing the arguments.
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* const getUser = memoize((id: number) => fetchUser(id));
|
|
387
|
+
* await getUser(1); // fetches
|
|
388
|
+
* await getUser(1); // returns cached result
|
|
389
|
+
*/
|
|
390
|
+
declare const memoize: <T extends (...args: any[]) => any>(fn: T, keyFn?: (...args: Parameters<T>) => string) => T & {
|
|
391
|
+
cache: Map<string, ReturnType<T>>;
|
|
392
|
+
clear: () => void;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* Returns a version of `fn` that executes exactly once.
|
|
396
|
+
* All subsequent calls return the result of the first call.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* const init = once(() => setupDatabase());
|
|
400
|
+
* await init(); // runs
|
|
401
|
+
* await init(); // returns cached result, does not run again
|
|
402
|
+
*/
|
|
403
|
+
declare const once: <T extends (...args: any[]) => any>(fn: T) => ((...args: Parameters<T>) => ReturnType<T>);
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Flattens a nested object into a single-level object with dot-notation keys.
|
|
407
|
+
*
|
|
408
|
+
* @example
|
|
409
|
+
* flattenObject({ a: { b: { c: 1 } } }) // { "a.b.c": 1 }
|
|
410
|
+
* flattenObject({ a: { b: 1 } }, { separator: "_" }) // { "a_b": 1 }
|
|
411
|
+
*/
|
|
412
|
+
declare const flattenObject: (obj: Record<string, any>, { separator, prefix }?: {
|
|
413
|
+
separator?: string;
|
|
414
|
+
prefix?: string;
|
|
415
|
+
}) => Record<string, any>;
|
|
416
|
+
/**
|
|
417
|
+
* Restores a flattened dot-notation object back to its nested form.
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* unflattenObject({ "a.b.c": 1 }) // { a: { b: { c: 1 } } }
|
|
421
|
+
*/
|
|
422
|
+
declare const unflattenObject: (obj: Record<string, any>, separator?: string) => Record<string, any>;
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* String utility functions
|
|
426
|
+
*/
|
|
427
|
+
declare const splitWords: (str: string) => string[];
|
|
428
|
+
declare const capitalize: (str: string) => string;
|
|
429
|
+
declare const toUpperCase: (str: string) => string;
|
|
430
|
+
declare const toLowerCase: (str: string) => string;
|
|
431
|
+
declare const camelCase: (str: string) => string;
|
|
432
|
+
declare const pascalCase: (str: string) => string;
|
|
433
|
+
declare const snakeCase: (str: string) => string;
|
|
434
|
+
declare const kebabCase: (str: string) => string;
|
|
435
|
+
/**
|
|
436
|
+
* Truncates a string to `length` characters, appending `suffix` if trimmed.
|
|
437
|
+
* The suffix length is included in the total, so the result never exceeds `length`.
|
|
438
|
+
*
|
|
439
|
+
* @example
|
|
440
|
+
* truncate("Hello, world!", 8) // "Hello..."
|
|
441
|
+
* truncate("Hello, world!", 8, " →") // "Hello →"
|
|
442
|
+
*/
|
|
443
|
+
declare const truncate: (str: string, length?: number, suffix?: string) => string;
|
|
444
|
+
/**
|
|
445
|
+
* Masks all but the last `visible` characters of a string.
|
|
446
|
+
* Useful for displaying sensitive values like credit cards or tokens.
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* maskString("4111111111111234") // "************1234"
|
|
450
|
+
* maskString("mysecrettoken", 6) // "*******secret" ← last 6 visible
|
|
451
|
+
*/
|
|
452
|
+
declare const maskString: (str: string, visible?: number) => string;
|
|
453
|
+
/**
|
|
454
|
+
* Returns true if the string contains only whitespace or is empty.
|
|
455
|
+
*/
|
|
456
|
+
declare const isBlank: (str: string) => boolean;
|
|
457
|
+
/**
|
|
458
|
+
* Reverses a string.
|
|
459
|
+
*/
|
|
460
|
+
declare const reverse: (str: string) => string;
|
|
461
|
+
/**
|
|
462
|
+
* Counts occurrences of `substr` within `str`.
|
|
463
|
+
*/
|
|
464
|
+
declare const countOccurrences: (str: string, substr: string) => number;
|
|
465
|
+
/**
|
|
466
|
+
* Removes all extra whitespace — trims the string and collapses
|
|
467
|
+
* internal sequences of whitespace down to a single space.
|
|
468
|
+
*
|
|
469
|
+
* @example
|
|
470
|
+
* normalizeWhitespace(" hello world ") // "hello world"
|
|
471
|
+
*/
|
|
472
|
+
declare const normalizeWhitespace: (str: string) => string;
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Returns true for plain objects (not arrays, dates, null, etc.)
|
|
476
|
+
*/
|
|
477
|
+
declare const isObject: (val: any) => boolean;
|
|
478
|
+
/**
|
|
479
|
+
* Validates an email address format.
|
|
480
|
+
*/
|
|
481
|
+
declare const isEmail: (value: string) => boolean;
|
|
482
|
+
/**
|
|
483
|
+
* Validates a UUID v1–v5 string.
|
|
484
|
+
*/
|
|
485
|
+
declare const isUUID: (value: string) => boolean;
|
|
486
|
+
/**
|
|
487
|
+
* Returns true for finite numbers (excludes NaN and Infinity).
|
|
488
|
+
*/
|
|
489
|
+
declare const isNumber: (value: any) => boolean;
|
|
490
|
+
/**
|
|
491
|
+
* Returns true if the string is valid parseable JSON.
|
|
492
|
+
*/
|
|
493
|
+
declare const isJSON: (value: string) => boolean;
|
|
494
|
+
/**
|
|
495
|
+
* Returns true for valid Date instances.
|
|
496
|
+
*/
|
|
497
|
+
declare const isDate: (value: any) => boolean;
|
|
498
|
+
/**
|
|
499
|
+
* Returns true for valid http/https URLs only.
|
|
500
|
+
* Rejects data:, javascript:, and other URI schemes.
|
|
501
|
+
*/
|
|
502
|
+
declare const isURL: (value: string) => boolean;
|
|
503
|
+
/**
|
|
504
|
+
* Returns true only for actual booleans (not truthy/falsy values).
|
|
505
|
+
*/
|
|
506
|
+
declare const isBoolean: (value: any) => boolean;
|
|
507
|
+
/**
|
|
508
|
+
* Returns true for strings.
|
|
509
|
+
*/
|
|
510
|
+
declare const isString: (value: any) => boolean;
|
|
511
|
+
/**
|
|
512
|
+
* Returns true for arrays.
|
|
513
|
+
*/
|
|
514
|
+
declare const isArray: (value: any) => boolean;
|
|
515
|
+
/**
|
|
516
|
+
* Returns true for integers (excludes floats, NaN, Infinity).
|
|
517
|
+
*/
|
|
518
|
+
declare const isInteger: (value: any) => boolean;
|
|
519
|
+
/**
|
|
520
|
+
* Returns true for positive numbers (excludes zero).
|
|
521
|
+
*/
|
|
522
|
+
declare const isPositive: (value: any) => boolean;
|
|
523
|
+
/**
|
|
524
|
+
* Returns true for negative numbers (excludes zero).
|
|
525
|
+
*/
|
|
526
|
+
declare const isNegative: (value: any) => boolean;
|
|
527
|
+
/**
|
|
528
|
+
* Returns true if value is null or undefined.
|
|
529
|
+
*/
|
|
530
|
+
declare const isNil: (value: any) => boolean;
|
|
531
|
+
/**
|
|
532
|
+
* Returns true if the value is "empty":
|
|
533
|
+
* - null / undefined
|
|
534
|
+
* - empty string or whitespace-only string
|
|
535
|
+
* - empty array
|
|
536
|
+
* - empty plain object
|
|
537
|
+
*/
|
|
538
|
+
declare const isEmpty: (val: any) => boolean;
|
|
539
|
+
|
|
540
|
+
interface Logger$1 {
|
|
541
|
+
info: (msg: string, meta?: any) => void;
|
|
542
|
+
error: (msg: string, meta?: any) => void;
|
|
543
|
+
warn: (msg: string, meta?: any) => void;
|
|
544
|
+
debug: (msg: string, meta?: any) => void;
|
|
545
|
+
}
|
|
134
546
|
declare class Redis {
|
|
135
|
-
client
|
|
136
|
-
|
|
547
|
+
private client;
|
|
548
|
+
private logger;
|
|
549
|
+
constructor(url: string, options?: RedisOptions, logger?: Logger$1);
|
|
137
550
|
private registerListeners;
|
|
138
551
|
start(): Promise<void>;
|
|
139
552
|
disconnect(): Promise<void>;
|
|
140
|
-
|
|
553
|
+
private validateKey;
|
|
554
|
+
private buildKey;
|
|
141
555
|
private serialize;
|
|
142
556
|
private deserialize;
|
|
143
557
|
set(key: string, data: any): Promise<"OK">;
|
|
144
558
|
setEx(key: string, data: any, duration: number | string): Promise<"OK">;
|
|
145
559
|
get<T = any>(key: string, parse?: boolean): Promise<T | null>;
|
|
146
560
|
delete(key: string): Promise<boolean>;
|
|
147
|
-
deleteAll(prefix: string): Promise<number>;
|
|
148
561
|
exists(key: string): Promise<boolean>;
|
|
149
562
|
ttl(key: string): Promise<number>;
|
|
150
563
|
expire(key: string, duration: number | string): Promise<boolean>;
|
|
151
|
-
|
|
564
|
+
/**
|
|
565
|
+
* Atomically increments a counter. Creates it at 1 if it doesn't exist.
|
|
566
|
+
* Optionally sets a TTL on first creation.
|
|
567
|
+
*
|
|
568
|
+
* @example
|
|
569
|
+
* await redis.increment("rate:user:123"); // 1, 2, 3...
|
|
570
|
+
* await redis.increment("rate:user:123", "1 hour"); // resets TTL each time
|
|
571
|
+
*/
|
|
572
|
+
increment(key: string, ttl?: number | string): Promise<number>;
|
|
573
|
+
/**
|
|
574
|
+
* Atomically decrements a counter.
|
|
575
|
+
*/
|
|
576
|
+
decrement(key: string): Promise<number>;
|
|
577
|
+
/**
|
|
578
|
+
* Sets one or more fields on a Redis hash.
|
|
579
|
+
*
|
|
580
|
+
* @example
|
|
581
|
+
* await redis.hset("user:1", { name: "Alice", role: "admin" });
|
|
582
|
+
*/
|
|
583
|
+
hset(key: string, data: Record<string, any>): Promise<number>;
|
|
584
|
+
/**
|
|
585
|
+
* Gets a single field from a Redis hash.
|
|
586
|
+
*/
|
|
587
|
+
hget<T = any>(key: string, field: string): Promise<T | null>;
|
|
588
|
+
/**
|
|
589
|
+
* Gets all fields from a Redis hash as a typed object.
|
|
590
|
+
*/
|
|
591
|
+
hgetAll<T extends Record<string, any> = Record<string, any>>(key: string): Promise<T | null>;
|
|
592
|
+
/**
|
|
593
|
+
* Deletes one or more fields from a Redis hash.
|
|
594
|
+
*/
|
|
595
|
+
hdel(key: string, ...fields: string[]): Promise<number>;
|
|
596
|
+
/**
|
|
597
|
+
* Safely scans for keys matching a pattern using SCAN (non-blocking).
|
|
598
|
+
* Prefer this over KEYS in production — KEYS blocks the event loop.
|
|
599
|
+
*
|
|
600
|
+
* @example
|
|
601
|
+
* await redis.scan("user:*") // ["user:1", "user:2", ...]
|
|
602
|
+
*/
|
|
603
|
+
scan(pattern: string): Promise<string[]>;
|
|
604
|
+
/**
|
|
605
|
+
* Deletes all keys matching a pattern using SCAN + batched DEL.
|
|
606
|
+
* Safe for large keyspaces.
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* await redis.deleteByPattern("session:*") // clears all sessions
|
|
610
|
+
*/
|
|
611
|
+
deleteByPattern(pattern: string): Promise<number>;
|
|
612
|
+
/**
|
|
613
|
+
* @deprecated Use `scan()` instead — KEYS blocks the Redis event loop.
|
|
614
|
+
*/
|
|
615
|
+
keys(pattern: string): Promise<string[]>;
|
|
616
|
+
/**
|
|
617
|
+
* @deprecated Use `deleteByPattern()` instead.
|
|
618
|
+
*/
|
|
619
|
+
deleteAll(prefix: string): Promise<number>;
|
|
620
|
+
/**
|
|
621
|
+
* Flushes the current database. Intended for testing only.
|
|
622
|
+
* Throws in production unless `force: true` is passed.
|
|
623
|
+
*/
|
|
624
|
+
flush(force?: boolean): Promise<void>;
|
|
625
|
+
private authKey;
|
|
626
|
+
private tokenKey;
|
|
152
627
|
getCachedUser<T = any>(id: string, throwError?: boolean): Promise<T | null>;
|
|
153
628
|
cacheUser(user: any, ttl?: number | string): Promise<void>;
|
|
629
|
+
/**
|
|
630
|
+
* Atomically updates an array field on a cached user.
|
|
631
|
+
* Operates on a fresh copy to avoid mutating the cached object before re-save.
|
|
632
|
+
*/
|
|
154
633
|
updateAuthData(userId: string, key: string, value: string, action?: "ADD" | "REMOVE"): Promise<any>;
|
|
155
634
|
private parseDuration;
|
|
156
635
|
}
|
|
157
636
|
|
|
158
|
-
interface SQSDequeueInt {
|
|
159
|
-
queueUrl: string;
|
|
160
|
-
consumerFunction: (message: any) => Promise<any>;
|
|
161
|
-
maxNumberOfMessages?: number;
|
|
162
|
-
waitTimeSeconds?: number;
|
|
163
|
-
dlqUrl?: string;
|
|
164
|
-
}
|
|
165
|
-
interface SQSEqueueInt {
|
|
166
|
-
queueUrl: string;
|
|
167
|
-
message: any;
|
|
168
|
-
}
|
|
169
637
|
interface Logger {
|
|
170
638
|
info(message: string, meta?: unknown): void;
|
|
171
639
|
error(message: string, meta?: unknown): void;
|
|
@@ -178,20 +646,314 @@ interface SqsConfig {
|
|
|
178
646
|
accessKeyId: string;
|
|
179
647
|
secretAccessKey: string;
|
|
180
648
|
}
|
|
649
|
+
interface SQSEnqueueOptions {
|
|
650
|
+
queueUrl: string;
|
|
651
|
+
message: string | object;
|
|
652
|
+
/** Required for FIFO queues */
|
|
653
|
+
messageGroupId?: string;
|
|
654
|
+
/** Required for FIFO queues with content-based deduplication disabled */
|
|
655
|
+
messageDeduplicationId?: string;
|
|
656
|
+
/** Delay message delivery (0–900 seconds) */
|
|
657
|
+
delaySeconds?: number;
|
|
658
|
+
/** Arbitrary metadata attached to the message */
|
|
659
|
+
attributes?: Record<string, MessageAttributeValue>;
|
|
660
|
+
}
|
|
661
|
+
interface SQSDequeueOptions {
|
|
662
|
+
queueUrl: string;
|
|
663
|
+
consumerFunction: (message: any) => Promise<void>;
|
|
664
|
+
dlqUrl?: string;
|
|
665
|
+
maxNumberOfMessages?: number;
|
|
666
|
+
waitTimeSeconds?: number;
|
|
667
|
+
/**
|
|
668
|
+
* Extend visibility timeout during processing (seconds).
|
|
669
|
+
* Set this close to your expected max processing time.
|
|
670
|
+
*/
|
|
671
|
+
visibilityTimeout?: number;
|
|
672
|
+
/**
|
|
673
|
+
* If true, failed messages are left in the queue for SQS to retry
|
|
674
|
+
* via the queue's own redrive policy instead of immediately going to DLQ.
|
|
675
|
+
*/
|
|
676
|
+
useRedrivePolicy?: boolean;
|
|
677
|
+
}
|
|
181
678
|
declare class SQS {
|
|
182
679
|
private client;
|
|
183
680
|
private logger;
|
|
681
|
+
private polling;
|
|
184
682
|
constructor(config: SqsConfig, logger?: Logger);
|
|
185
|
-
|
|
186
|
-
|
|
683
|
+
/**
|
|
684
|
+
* Sends a message to an SQS queue.
|
|
685
|
+
* Automatically serializes objects to JSON.
|
|
686
|
+
*
|
|
687
|
+
* @example
|
|
688
|
+
* await sqs.enqueue({ queueUrl, message: { event: "user.created", userId: 1 } });
|
|
689
|
+
*/
|
|
690
|
+
enqueue({ queueUrl, message, messageGroupId, messageDeduplicationId, delaySeconds, attributes, }: SQSEnqueueOptions): Promise<boolean>;
|
|
691
|
+
/**
|
|
692
|
+
* Starts long-polling a queue and passes each message to `consumerFunction`.
|
|
693
|
+
* Runs until `stop()` is called.
|
|
694
|
+
*
|
|
695
|
+
* Delete behaviour:
|
|
696
|
+
* - On success → always deletes
|
|
697
|
+
* - On failure + DLQ → moves to DLQ, then deletes
|
|
698
|
+
* - On failure + useRedrivePolicy → does NOT delete (lets SQS retry)
|
|
699
|
+
* - On failure + no DLQ + no redrive → logs and deletes to avoid poison pill loop
|
|
700
|
+
*/
|
|
701
|
+
dequeue({ queueUrl, consumerFunction, dlqUrl, maxNumberOfMessages, waitTimeSeconds, visibilityTimeout, useRedrivePolicy, }: SQSDequeueOptions): Promise<void>;
|
|
702
|
+
/**
|
|
703
|
+
* Gracefully stops the polling loop after the current batch completes.
|
|
704
|
+
*/
|
|
705
|
+
stop(): void;
|
|
706
|
+
private processMessage;
|
|
187
707
|
}
|
|
188
708
|
|
|
709
|
+
type LogLevel = "error" | "warn" | "info" | "http" | "debug";
|
|
710
|
+
interface WinstonLoggerOptions {
|
|
711
|
+
/** Minimum log level to output (default: "info", or "debug" in development) */
|
|
712
|
+
level?: LogLevel;
|
|
713
|
+
/** Service name attached to every log entry */
|
|
714
|
+
service?: string;
|
|
715
|
+
/** If true, write logs to a file in addition to the console */
|
|
716
|
+
file?: {
|
|
717
|
+
path: string;
|
|
718
|
+
/** Separate file for errors only (recommended) */
|
|
719
|
+
errorPath?: string;
|
|
720
|
+
};
|
|
721
|
+
/** If true, output plain text instead of JSON (default: true in development) */
|
|
722
|
+
pretty?: boolean;
|
|
723
|
+
/** Static metadata attached to every log entry */
|
|
724
|
+
defaultMeta?: Record<string, unknown>;
|
|
725
|
+
}
|
|
189
726
|
declare class WinstonLogger implements Logger {
|
|
190
727
|
private logger;
|
|
728
|
+
constructor(options?: WinstonLoggerOptions);
|
|
191
729
|
info(message: string, meta?: unknown): void;
|
|
192
730
|
error(message: string, meta?: unknown): void;
|
|
193
731
|
warn(message: string, meta?: unknown): void;
|
|
194
732
|
debug(message: string, meta?: unknown): void;
|
|
733
|
+
http(message: string, meta?: unknown): void;
|
|
734
|
+
/**
|
|
735
|
+
* Returns a child logger with additional metadata attached to every entry.
|
|
736
|
+
* Useful for scoping logs to a request, service, or job.
|
|
737
|
+
*
|
|
738
|
+
* @example
|
|
739
|
+
* const log = logger.child({ requestId: "abc-123", userId: "u-1" });
|
|
740
|
+
* log.info("User fetched"); // → { requestId: "abc-123", userId: "u-1", message: "User fetched" }
|
|
741
|
+
*/
|
|
742
|
+
child(meta: Record<string, unknown>): WinstonLogger;
|
|
743
|
+
/**
|
|
744
|
+
* Dynamically changes the log level at runtime.
|
|
745
|
+
* Useful for temporarily enabling debug logs in production.
|
|
746
|
+
*
|
|
747
|
+
* @example
|
|
748
|
+
* logger.setLevel("debug");
|
|
749
|
+
*/
|
|
750
|
+
setLevel(level: LogLevel): void;
|
|
751
|
+
/**
|
|
752
|
+
* Returns true if the given level would currently be logged.
|
|
753
|
+
*
|
|
754
|
+
* @example
|
|
755
|
+
* if (logger.isLevelEnabled("debug")) { ... }
|
|
756
|
+
*/
|
|
757
|
+
isLevelEnabled(level: LogLevel): boolean;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
interface S3Config {
|
|
761
|
+
region: string;
|
|
762
|
+
accessKeyId: string;
|
|
763
|
+
secretAccessKey: string;
|
|
764
|
+
defaultBucket?: string;
|
|
765
|
+
}
|
|
766
|
+
interface S3UploadOptions {
|
|
767
|
+
bucket?: string;
|
|
768
|
+
key: string;
|
|
769
|
+
body: Buffer | Uint8Array | string | Readable;
|
|
770
|
+
contentType?: string;
|
|
771
|
+
metadata?: Record<string, string>;
|
|
772
|
+
/** Canned ACL e.g. "private" | "public-read" */
|
|
773
|
+
acl?: ObjectCannedACL;
|
|
774
|
+
}
|
|
775
|
+
interface S3UploadResult {
|
|
776
|
+
bucket: string;
|
|
777
|
+
key: string;
|
|
778
|
+
url: string;
|
|
779
|
+
}
|
|
780
|
+
interface S3ObjectOptions {
|
|
781
|
+
bucket?: string;
|
|
782
|
+
key: string;
|
|
783
|
+
}
|
|
784
|
+
interface S3CopyOptions {
|
|
785
|
+
sourceBucket?: string;
|
|
786
|
+
sourceKey: string;
|
|
787
|
+
destinationBucket?: string;
|
|
788
|
+
destinationKey: string;
|
|
789
|
+
}
|
|
790
|
+
interface S3SignedUrlOptions {
|
|
791
|
+
bucket?: string;
|
|
792
|
+
key: string;
|
|
793
|
+
expiresIn?: number;
|
|
794
|
+
}
|
|
795
|
+
declare class S3 {
|
|
796
|
+
private client;
|
|
797
|
+
private logger;
|
|
798
|
+
private defaultBucket?;
|
|
799
|
+
private region;
|
|
800
|
+
constructor(config: S3Config, logger?: Logger);
|
|
801
|
+
private getBucket;
|
|
802
|
+
private getObjectUrl;
|
|
803
|
+
private streamToBuffer;
|
|
804
|
+
/**
|
|
805
|
+
* Uploads a file to S3. Returns the bucket, key, and public URL.
|
|
806
|
+
*
|
|
807
|
+
* @example
|
|
808
|
+
* const result = await s3.upload({ key: "avatars/user-1.png", body: buffer, contentType: "image/png" });
|
|
809
|
+
* result.url // "https://my-bucket.s3.us-east-1.amazonaws.com/avatars/user-1.png"
|
|
810
|
+
*/
|
|
811
|
+
upload({ bucket, key, body, contentType, metadata, acl, }: S3UploadOptions): Promise<S3UploadResult>;
|
|
812
|
+
/**
|
|
813
|
+
* Downloads an S3 object and returns it as a Buffer.
|
|
814
|
+
*/
|
|
815
|
+
download({ bucket, key }: S3ObjectOptions): Promise<Buffer>;
|
|
816
|
+
/**
|
|
817
|
+
* Returns the raw readable stream for an S3 object.
|
|
818
|
+
* Prefer this over `download` for large files.
|
|
819
|
+
*/
|
|
820
|
+
stream({ bucket, key }: S3ObjectOptions): Promise<Readable>;
|
|
821
|
+
delete({ bucket, key }: S3ObjectOptions): Promise<boolean>;
|
|
822
|
+
/**
|
|
823
|
+
* Copies an object within S3 — within the same bucket or across buckets.
|
|
824
|
+
*
|
|
825
|
+
* @example
|
|
826
|
+
* await s3.copy({ sourceKey: "uploads/tmp.png", destinationKey: "avatars/user-1.png" });
|
|
827
|
+
*/
|
|
828
|
+
copy({ sourceBucket, sourceKey, destinationBucket, destinationKey, }: S3CopyOptions): Promise<S3UploadResult>;
|
|
829
|
+
/**
|
|
830
|
+
* Returns true if the object exists.
|
|
831
|
+
* Throws on non-404 errors (permissions, network) rather than silently returning false.
|
|
832
|
+
*/
|
|
833
|
+
exists({ bucket, key }: S3ObjectOptions): Promise<boolean>;
|
|
834
|
+
/**
|
|
835
|
+
* Generates a pre-signed URL for downloading an object (GET).
|
|
836
|
+
* Default expiry: 1 hour.
|
|
837
|
+
*/
|
|
838
|
+
getSignedDownloadUrl({ bucket, key, expiresIn }: S3SignedUrlOptions): Promise<string>;
|
|
839
|
+
/**
|
|
840
|
+
* Generates a pre-signed URL for uploading an object directly (PUT).
|
|
841
|
+
* Use this for browser → S3 direct uploads without proxying through your server.
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* const url = await s3.getSignedUploadUrl({ key: "avatars/user-1.png", contentType: "image/png" });
|
|
845
|
+
* // Client does: fetch(url, { method: "PUT", body: file })
|
|
846
|
+
*/
|
|
847
|
+
getSignedUploadUrl({ bucket, key, expiresIn, contentType, }: S3SignedUrlOptions & {
|
|
848
|
+
contentType?: string;
|
|
849
|
+
}): Promise<string>;
|
|
850
|
+
/**
|
|
851
|
+
* Returns a scoped helper with the bucket pre-filled.
|
|
852
|
+
*
|
|
853
|
+
* @example
|
|
854
|
+
* const avatars = s3.bucket("my-avatars-bucket");
|
|
855
|
+
* await avatars.upload({ key: "user-1.png", body: buffer });
|
|
856
|
+
*/
|
|
857
|
+
bucket(bucketName: string): {
|
|
858
|
+
upload: (opts: Omit<S3UploadOptions, "bucket">) => Promise<S3UploadResult>;
|
|
859
|
+
download: (opts: Omit<S3ObjectOptions, "bucket">) => Promise<Buffer<ArrayBufferLike>>;
|
|
860
|
+
stream: (opts: Omit<S3ObjectOptions, "bucket">) => Promise<Readable>;
|
|
861
|
+
delete: (opts: Omit<S3ObjectOptions, "bucket">) => Promise<boolean>;
|
|
862
|
+
exists: (opts: Omit<S3ObjectOptions, "bucket">) => Promise<boolean>;
|
|
863
|
+
copy: (opts: Omit<S3CopyOptions, "destinationBucket">) => Promise<S3UploadResult>;
|
|
864
|
+
getSignedDownloadUrl: (opts: Omit<S3SignedUrlOptions, "bucket">) => Promise<string>;
|
|
865
|
+
getSignedUploadUrl: (opts: Omit<S3SignedUrlOptions & {
|
|
866
|
+
contentType?: string;
|
|
867
|
+
}, "bucket">) => Promise<string>;
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
interface CronJobOptions {
|
|
872
|
+
/** Cron expression e.g. "0 * * * *" or human shorthand */
|
|
873
|
+
schedule: string;
|
|
874
|
+
/** Human-readable name for logging and lookup */
|
|
875
|
+
name: string;
|
|
876
|
+
/** The function to execute on each tick */
|
|
877
|
+
handler: () => Promise<void> | void;
|
|
878
|
+
/** If true, runs the handler immediately on registration (default: false) */
|
|
879
|
+
runOnInit?: boolean;
|
|
880
|
+
/** Timezone e.g. "America/New_York" (default: system timezone) */
|
|
881
|
+
timezone?: string;
|
|
882
|
+
/** If true, prevents overlapping executions — waits for current run to finish (default: true) */
|
|
883
|
+
preventOverlap?: boolean;
|
|
884
|
+
}
|
|
885
|
+
interface CronJobStatus {
|
|
886
|
+
name: string;
|
|
887
|
+
schedule: string;
|
|
888
|
+
running: boolean;
|
|
889
|
+
lastRun: Date | null;
|
|
890
|
+
lastError: Error | null;
|
|
891
|
+
executionCount: number;
|
|
892
|
+
errorCount: number;
|
|
893
|
+
}
|
|
894
|
+
declare class Cron {
|
|
895
|
+
private jobs;
|
|
896
|
+
private logger;
|
|
897
|
+
constructor(logger?: Logger);
|
|
898
|
+
/**
|
|
899
|
+
* Registers and starts a cron job.
|
|
900
|
+
*
|
|
901
|
+
* @example
|
|
902
|
+
* cron.register({
|
|
903
|
+
* name: "send-digest",
|
|
904
|
+
* schedule: "every day at noon",
|
|
905
|
+
* handler: async () => { await sendDigestEmails(); },
|
|
906
|
+
* timezone: "America/New_York",
|
|
907
|
+
* });
|
|
908
|
+
*/
|
|
909
|
+
register(options: CronJobOptions): void;
|
|
910
|
+
private execute;
|
|
911
|
+
/**
|
|
912
|
+
* Stops a running job without removing it.
|
|
913
|
+
* Can be resumed with start().
|
|
914
|
+
*/
|
|
915
|
+
stop(name: string): void;
|
|
916
|
+
/**
|
|
917
|
+
* Resumes a stopped job.
|
|
918
|
+
*/
|
|
919
|
+
start(name: string): void;
|
|
920
|
+
/**
|
|
921
|
+
* Stops and removes a job entirely.
|
|
922
|
+
*/
|
|
923
|
+
remove(name: string): void;
|
|
924
|
+
/**
|
|
925
|
+
* Replaces an existing job with a new configuration.
|
|
926
|
+
* Useful for updating schedules at runtime.
|
|
927
|
+
*/
|
|
928
|
+
replace(options: CronJobOptions): void;
|
|
929
|
+
/**
|
|
930
|
+
* Manually triggers a job outside its schedule.
|
|
931
|
+
* Respects preventOverlap.
|
|
932
|
+
*
|
|
933
|
+
* @example
|
|
934
|
+
* await cron.run("send-digest");
|
|
935
|
+
*/
|
|
936
|
+
run(name: string): Promise<void>;
|
|
937
|
+
/**
|
|
938
|
+
* Stops all registered jobs. Call this on process shutdown.
|
|
939
|
+
*
|
|
940
|
+
* @example
|
|
941
|
+
* process.on("SIGTERM", () => cron.stopAll());
|
|
942
|
+
*/
|
|
943
|
+
stopAll(): void;
|
|
944
|
+
/**
|
|
945
|
+
* Returns the status of a single job.
|
|
946
|
+
*/
|
|
947
|
+
status(name: string): CronJobStatus;
|
|
948
|
+
/**
|
|
949
|
+
* Returns the status of all registered jobs.
|
|
950
|
+
*/
|
|
951
|
+
statusAll(): CronJobStatus[];
|
|
952
|
+
/**
|
|
953
|
+
* Returns true if a job with the given name is registered.
|
|
954
|
+
*/
|
|
955
|
+
has(name: string): boolean;
|
|
956
|
+
private getJob;
|
|
195
957
|
}
|
|
196
958
|
|
|
197
959
|
interface JwtEncodeOptions {
|
|
@@ -206,8 +968,129 @@ interface JwtDecodeOptions {
|
|
|
206
968
|
algorithms?: string[];
|
|
207
969
|
}
|
|
208
970
|
declare const jwtService: {
|
|
971
|
+
/**
|
|
972
|
+
* Signs a payload and returns a JWT string.
|
|
973
|
+
*
|
|
974
|
+
* @example
|
|
975
|
+
* const token = await jwtService.encode({ data: { userId: 1 }, secretKey: "secret" });
|
|
976
|
+
*/
|
|
209
977
|
encode({ data, secretKey, expiresIn, algorithm, }: JwtEncodeOptions): Promise<string>;
|
|
978
|
+
/**
|
|
979
|
+
* Verifies and decodes a JWT string.
|
|
980
|
+
* Throws a typed `JwtError` on expiry, invalid signature, or not-yet-valid tokens.
|
|
981
|
+
*
|
|
982
|
+
* @example
|
|
983
|
+
* const payload = await jwtService.decode<{ userId: number }>({ token, secretKey: "secret" });
|
|
984
|
+
*/
|
|
210
985
|
decode<T = jwt.JwtPayload>({ token, secretKey, algorithms, }: JwtDecodeOptions): Promise<T>;
|
|
986
|
+
/**
|
|
987
|
+
* Returns the expiry date of a token without verifying it.
|
|
988
|
+
* Returns null if the token has no expiry or cannot be decoded.
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* jwtService.getExpiry(token) // Date | null
|
|
992
|
+
*/
|
|
993
|
+
getExpiry(token: string): Date | null;
|
|
994
|
+
/**
|
|
995
|
+
* Returns true if the token is expired, without verifying the signature.
|
|
996
|
+
* Useful for checking whether to refresh a token before making a request.
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* if (jwtService.isExpired(token)) { ... }
|
|
1000
|
+
*/
|
|
1001
|
+
isExpired(token: string): boolean;
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
interface HashOptions {
|
|
1005
|
+
/** bcrypt salt rounds (default: 12) */
|
|
1006
|
+
rounds?: number;
|
|
1007
|
+
}
|
|
1008
|
+
interface HmacOptions {
|
|
1009
|
+
/** HMAC algorithm (default: "sha256") */
|
|
1010
|
+
algorithm?: "sha256" | "sha512" | "sha1";
|
|
1011
|
+
/** Output encoding (default: "hex") */
|
|
1012
|
+
encoding?: "hex" | "base64";
|
|
1013
|
+
}
|
|
1014
|
+
interface TokenOptions {
|
|
1015
|
+
/** Byte length of the random token (default: 32) */
|
|
1016
|
+
bytes?: number;
|
|
1017
|
+
/** Output encoding (default: "hex") */
|
|
1018
|
+
encoding?: "hex" | "base64url" | "base64";
|
|
1019
|
+
}
|
|
1020
|
+
declare const hashService: {
|
|
1021
|
+
/**
|
|
1022
|
+
* Hashes a plain text value using bcrypt.
|
|
1023
|
+
* Use for passwords — bcrypt is intentionally slow and salted.
|
|
1024
|
+
*
|
|
1025
|
+
* @example
|
|
1026
|
+
* const hashed = await hashService.hash("myPassword123");
|
|
1027
|
+
*/
|
|
1028
|
+
hash(plain: string, { rounds }?: HashOptions): Promise<string>;
|
|
1029
|
+
/**
|
|
1030
|
+
* Compares a plain text value against a bcrypt hash.
|
|
1031
|
+
*
|
|
1032
|
+
* @example
|
|
1033
|
+
* const match = await hashService.compare("myPassword123", storedHash);
|
|
1034
|
+
* if (!match) throw new AuthenticationError("Invalid credentials");
|
|
1035
|
+
*/
|
|
1036
|
+
compare(plain: string, hashed: string): Promise<boolean>;
|
|
1037
|
+
/**
|
|
1038
|
+
* Returns true if the string looks like a bcrypt hash.
|
|
1039
|
+
* Useful for detecting already-hashed values before double-hashing.
|
|
1040
|
+
*
|
|
1041
|
+
* @example
|
|
1042
|
+
* hashService.isBcryptHash("$2b$12$...") // true
|
|
1043
|
+
*/
|
|
1044
|
+
isBcryptHash(value: string): boolean;
|
|
1045
|
+
/**
|
|
1046
|
+
* Creates an HMAC signature for a value using a secret key.
|
|
1047
|
+
* Use for signing data (webhooks, tokens, URLs) — NOT for passwords.
|
|
1048
|
+
*
|
|
1049
|
+
* @example
|
|
1050
|
+
* const sig = hashService.hmac("payload body", process.env.WEBHOOK_SECRET);
|
|
1051
|
+
*/
|
|
1052
|
+
hmac(value: string, secret: string, { algorithm, encoding }?: HmacOptions): string;
|
|
1053
|
+
/**
|
|
1054
|
+
* Verifies an HMAC signature using a timing-safe comparison.
|
|
1055
|
+
* Always use this instead of `===` to prevent timing attacks.
|
|
1056
|
+
*
|
|
1057
|
+
* @example
|
|
1058
|
+
* const valid = hashService.verifyHmac(payload, secret, incomingSignature);
|
|
1059
|
+
* if (!valid) throw new Error("Invalid webhook signature");
|
|
1060
|
+
*/
|
|
1061
|
+
verifyHmac(value: string, secret: string, signature: string, options?: HmacOptions): boolean;
|
|
1062
|
+
/**
|
|
1063
|
+
* Creates a one-way SHA hash of a value (no secret).
|
|
1064
|
+
* Use for content fingerprinting, cache keys, or deduplication.
|
|
1065
|
+
* NOT suitable for passwords.
|
|
1066
|
+
*
|
|
1067
|
+
* @example
|
|
1068
|
+
* const fingerprint = hashService.sha256("file contents here");
|
|
1069
|
+
*/
|
|
1070
|
+
sha256(value: string, encoding?: "hex" | "base64"): string;
|
|
1071
|
+
sha512(value: string, encoding?: "hex" | "base64"): string;
|
|
1072
|
+
/**
|
|
1073
|
+
* Generates a cryptographically secure random token.
|
|
1074
|
+
* Use for password reset tokens, email verification, API keys, etc.
|
|
1075
|
+
*
|
|
1076
|
+
* @example
|
|
1077
|
+
* const token = hashService.generateToken(); // 64-char hex string
|
|
1078
|
+
* const token = hashService.generateToken({ bytes: 16, encoding: "base64url" });
|
|
1079
|
+
*/
|
|
1080
|
+
generateToken({ bytes, encoding }?: TokenOptions): string;
|
|
1081
|
+
/**
|
|
1082
|
+
* Generates a token and returns both the raw value (to send to user)
|
|
1083
|
+
* and its SHA-256 hash (to store in the database).
|
|
1084
|
+
*
|
|
1085
|
+
* @example
|
|
1086
|
+
* const { token, hashed } = hashService.generateHashedToken();
|
|
1087
|
+
* await db.user.update({ resetToken: hashed, resetTokenExpiry: ... });
|
|
1088
|
+
* await email.send({ to: user.email, token }); // send raw token to user
|
|
1089
|
+
*/
|
|
1090
|
+
generateHashedToken(options?: TokenOptions): {
|
|
1091
|
+
token: string;
|
|
1092
|
+
hashed: string;
|
|
1093
|
+
};
|
|
211
1094
|
};
|
|
212
1095
|
|
|
213
|
-
export { AppError, AuthenticationError, AuthorizationError, BadRequestError, ExistingError, HTTP_STATUS, HTTP_STATUS_CODE_ERROR, HttpStatus, HttpStatusKey, JwtDecodeOptions, JwtEncodeOptions, NoContent, NotFoundError, Redis, SQS, ServerError, SqsConfig, TokenExpiredError, TokenInvalidError, ValidationError, WinstonLogger, capitalize, errorHandler, expressErrorMiddleware, formatDate, isEmpty, isObject,
|
|
1096
|
+
export { AppError, AuthenticationError, AuthorizationError, BadRequestError, Cron, CronJobOptions, CronJobStatus, DebouncedFn, DirectConstraint, ExistingError, FieldConstraint, HTTP_STATUS, HTTP_STATUS_CODE_ERROR, HashOptions, HmacOptions, HttpStatus, HttpStatusKey, JoiConstraints, JwtDecodeOptions, JwtEncodeOptions, LogLevel, NoContent, NotFoundError, Redis, RetryOptions, S3, S3Config, S3CopyOptions, S3ObjectOptions, S3SignedUrlOptions, S3UploadOptions, S3UploadResult, SQS, SQSDequeueOptions, SQSEnqueueOptions, ServerError, SqsConfig, ThrottledFn, TokenExpiredError, TokenInvalidError, TokenOptions, ValidationError, WinstonLogger, WinstonLoggerOptions, camelCase, capitalize, countOccurrences, debounce, errorHandler, expressErrorMiddleware, flattenObject, formatDate, hashService, isArray, isBlank, isBoolean, isDate, isEmail, isEmpty, isInteger, isJSON, isNegative, isNil, isNumber, isObject, isPositive, isString, isURL, isUUID, joiMiddleware, joiValidate, jwtService, kebabCase, makeRequest, maskString, memoize, normalizeWhitespace, once, paginate, parseJSON, pascalCase, retry, reverse, sleep, snakeCase, splitWords, stringifyJSON, throttle, timeout, toLowerCase, toUpperCase, truncate, unflattenObject, uuid };
|