@noambz/app-utils 1.0.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.
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Format a date for a specific timezone and locale.
3
+ * Defaults to "now" when no date is provided.
4
+ *
5
+ * @example formatDate("Asia/Jerusalem", "he-IL") // "30.3.2026, 14:05:30"
6
+ * @example formatDate("Asia/Jerusalem", "he-IL", someDate)
7
+ */
8
+ export declare function formatDate(tz: string, locale?: string, date?: Date): string;
9
+ /** @deprecated Use `formatDate` instead. */
10
+ export declare const nowFormatted: typeof formatDate;
11
+ /**
12
+ * Get today's date as an ISO string (YYYY-MM-DD) in the given timezone.
13
+ * Falls back to UTC if no timezone is provided.
14
+ */
15
+ export declare function todayISO(tz?: string): string;
16
+ /**
17
+ * Get the current time as HH:mm in the given timezone.
18
+ */
19
+ export declare function timeHHMM(tz: string): string;
20
+ /**
21
+ * Get both the calendar date (YYYY-MM-DD) and the hour (0-23)
22
+ * for a given point in time in a specific timezone.
23
+ *
24
+ * Useful for scheduling daily summaries / actions by local hour.
25
+ */
26
+ export declare function getZonedDateAndHour(tz: string, when?: Date): {
27
+ dateKey: string;
28
+ hour: number;
29
+ };
30
+ //# sourceMappingURL=dates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.d.ts","sourceRoot":"","sources":["../src/dates.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,SAAU,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,MAAM,CAE5E;AAED,4CAA4C;AAC5C,eAAO,MAAM,YAAY,mBAAa,CAAC;AAEvC;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAU5C;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAO3C;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,MAAM,EACV,IAAI,GAAE,IAAiB,GACtB;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAgBnC"}
package/dist/dates.js ADDED
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nowFormatted = void 0;
4
+ exports.formatDate = formatDate;
5
+ exports.todayISO = todayISO;
6
+ exports.timeHHMM = timeHHMM;
7
+ exports.getZonedDateAndHour = getZonedDateAndHour;
8
+ /**
9
+ * Format a date for a specific timezone and locale.
10
+ * Defaults to "now" when no date is provided.
11
+ *
12
+ * @example formatDate("Asia/Jerusalem", "he-IL") // "30.3.2026, 14:05:30"
13
+ * @example formatDate("Asia/Jerusalem", "he-IL", someDate)
14
+ */
15
+ function formatDate(tz, locale = "en-GB", date) {
16
+ return (date ?? new Date()).toLocaleString(locale, { timeZone: tz });
17
+ }
18
+ /** @deprecated Use `formatDate` instead. */
19
+ exports.nowFormatted = formatDate;
20
+ /**
21
+ * Get today's date as an ISO string (YYYY-MM-DD) in the given timezone.
22
+ * Falls back to UTC if no timezone is provided.
23
+ */
24
+ function todayISO(tz) {
25
+ if (!tz)
26
+ return new Date().toISOString().slice(0, 10);
27
+ const parts = new Intl.DateTimeFormat("en-CA", {
28
+ timeZone: tz,
29
+ year: "numeric",
30
+ month: "2-digit",
31
+ day: "2-digit",
32
+ }).formatToParts(new Date());
33
+ const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
34
+ return `${get("year")}-${get("month")}-${get("day")}`;
35
+ }
36
+ /**
37
+ * Get the current time as HH:mm in the given timezone.
38
+ */
39
+ function timeHHMM(tz) {
40
+ return new Date().toLocaleTimeString("en-GB", {
41
+ timeZone: tz,
42
+ hour: "2-digit",
43
+ minute: "2-digit",
44
+ hour12: false,
45
+ });
46
+ }
47
+ /**
48
+ * Get both the calendar date (YYYY-MM-DD) and the hour (0-23)
49
+ * for a given point in time in a specific timezone.
50
+ *
51
+ * Useful for scheduling daily summaries / actions by local hour.
52
+ */
53
+ function getZonedDateAndHour(tz, when = new Date()) {
54
+ const parts = new Intl.DateTimeFormat("en-US", {
55
+ timeZone: tz,
56
+ year: "numeric",
57
+ month: "2-digit",
58
+ day: "2-digit",
59
+ hour: "numeric",
60
+ hour12: false,
61
+ }).formatToParts(when);
62
+ const get = (type) => parts.find((p) => p.type === type)?.value ?? "";
63
+ const y = get("year");
64
+ const m = get("month");
65
+ const d = get("day");
66
+ const hour = parseInt(get("hour"), 10);
67
+ return { dateKey: `${y}-${m}-${d}`, hour: Number.isNaN(hour) ? 0 : hour };
68
+ }
69
+ //# sourceMappingURL=dates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.js","sourceRoot":"","sources":["../src/dates.ts"],"names":[],"mappings":";;;AAOA,gCAEC;AASD,4BAUC;AAKD,4BAOC;AAQD,kDAmBC;AAnED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,EAAU,EAAE,MAAM,GAAG,OAAO,EAAE,IAAW;IAClE,OAAO,CAAC,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;AACvE,CAAC;AAED,4CAA4C;AAC/B,QAAA,YAAY,GAAG,UAAU,CAAC;AAEvC;;;GAGG;AACH,SAAgB,QAAQ,CAAC,EAAW;IAClC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QAC7C,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;KACf,CAAC,CAAC,aAAa,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;IAC9E,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,SAAgB,QAAQ,CAAC,EAAU;IACjC,OAAO,IAAI,IAAI,EAAE,CAAC,kBAAkB,CAAC,OAAO,EAAE;QAC5C,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;QACjB,MAAM,EAAE,KAAK;KACd,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,SAAgB,mBAAmB,CACjC,EAAU,EACV,OAAa,IAAI,IAAI,EAAE;IAEvB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;QAC7C,QAAQ,EAAE,EAAE;QACZ,IAAI,EAAE,SAAS;QACf,KAAK,EAAE,SAAS;QAChB,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,KAAK;KACd,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;IAEvB,MAAM,GAAG,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,KAAK,IAAI,EAAE,CAAC;IAC9E,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;IACtB,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC;IACrB,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;IACvC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,12 @@
1
+ export interface FetchRetryOptions {
2
+ maxRetries?: number;
3
+ baseDelayMs?: number;
4
+ /** Optional log function called on retries. */
5
+ onRetry?: (attempt: number, delayMs: number, reason: string) => void;
6
+ }
7
+ /**
8
+ * Fetch with automatic retries on 429 (rate-limit) and network errors.
9
+ * Uses exponential backoff.
10
+ */
11
+ export declare function fetchWithRetry(url: string, init?: RequestInit, options?: FetchRetryOptions): Promise<Response>;
12
+ //# sourceMappingURL=fetch-retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-retry.d.ts","sourceRoot":"","sources":["../src/fetch-retry.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACtE;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,QAAQ,CAAC,CA4BnB"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchWithRetry = fetchWithRetry;
4
+ /**
5
+ * Fetch with automatic retries on 429 (rate-limit) and network errors.
6
+ * Uses exponential backoff.
7
+ */
8
+ async function fetchWithRetry(url, init, options) {
9
+ const maxRetries = options?.maxRetries ?? 3;
10
+ const baseDelayMs = options?.baseDelayMs ?? 1000;
11
+ let lastError;
12
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
13
+ try {
14
+ const res = await fetch(url, init);
15
+ if (res.status === 429 && attempt < maxRetries) {
16
+ const delay = baseDelayMs * Math.pow(2, attempt);
17
+ options?.onRetry?.(attempt + 1, delay, "429 rate-limited");
18
+ await sleep(delay);
19
+ continue;
20
+ }
21
+ return res;
22
+ }
23
+ catch (err) {
24
+ lastError = err instanceof Error ? err : new Error(String(err));
25
+ if (attempt < maxRetries) {
26
+ const delay = baseDelayMs * Math.pow(2, attempt);
27
+ options?.onRetry?.(attempt + 1, delay, lastError.message);
28
+ await sleep(delay);
29
+ }
30
+ }
31
+ }
32
+ throw lastError ?? new Error("fetchWithRetry exhausted all retries");
33
+ }
34
+ function sleep(ms) {
35
+ return new Promise((resolve) => setTimeout(resolve, ms));
36
+ }
37
+ //# sourceMappingURL=fetch-retry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch-retry.js","sourceRoot":"","sources":["../src/fetch-retry.ts"],"names":[],"mappings":";;AAWA,wCAgCC;AApCD;;;GAGG;AACI,KAAK,UAAU,cAAc,CAClC,GAAW,EACX,IAAkB,EAClB,OAA2B;IAE3B,MAAM,UAAU,GAAG,OAAO,EAAE,UAAU,IAAI,CAAC,CAAC;IAC5C,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,IAAI,IAAI,CAAC;IACjD,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;YAEnC,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBAC/C,MAAM,KAAK,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;gBACjD,OAAO,EAAE,OAAO,EAAE,CAAC,OAAO,GAAG,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC;gBAC3D,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;gBACnB,SAAS;YACX,CAAC;YAED,OAAO,GAAG,CAAC;QACb,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;gBACzB,MAAM,KAAK,GAAG,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC;gBACjD,OAAO,EAAE,OAAO,EAAE,CAAC,OAAO,GAAG,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;gBAC1D,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,SAAS,IAAI,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC;AACvE,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { createLogger } from "./logger";
2
+ export type { CreateLoggerOptions } from "./logger";
3
+ export { fetchWithRetry } from "./fetch-retry";
4
+ export type { FetchRetryOptions } from "./fetch-retry";
5
+ export { nowFormatted, todayISO, timeHHMM, getZonedDateAndHour } from "./dates";
6
+ export { sleep } from "./sleep";
7
+ export { checkRateLimit } from "./rate-limit";
8
+ export type { RateLimitResult } from "./rate-limit";
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAEhF,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkRateLimit = exports.sleep = exports.getZonedDateAndHour = exports.timeHHMM = exports.todayISO = exports.nowFormatted = exports.fetchWithRetry = exports.createLogger = void 0;
4
+ var logger_1 = require("./logger");
5
+ Object.defineProperty(exports, "createLogger", { enumerable: true, get: function () { return logger_1.createLogger; } });
6
+ var fetch_retry_1 = require("./fetch-retry");
7
+ Object.defineProperty(exports, "fetchWithRetry", { enumerable: true, get: function () { return fetch_retry_1.fetchWithRetry; } });
8
+ var dates_1 = require("./dates");
9
+ Object.defineProperty(exports, "nowFormatted", { enumerable: true, get: function () { return dates_1.nowFormatted; } });
10
+ Object.defineProperty(exports, "todayISO", { enumerable: true, get: function () { return dates_1.todayISO; } });
11
+ Object.defineProperty(exports, "timeHHMM", { enumerable: true, get: function () { return dates_1.timeHHMM; } });
12
+ Object.defineProperty(exports, "getZonedDateAndHour", { enumerable: true, get: function () { return dates_1.getZonedDateAndHour; } });
13
+ var sleep_1 = require("./sleep");
14
+ Object.defineProperty(exports, "sleep", { enumerable: true, get: function () { return sleep_1.sleep; } });
15
+ var rate_limit_1 = require("./rate-limit");
16
+ Object.defineProperty(exports, "checkRateLimit", { enumerable: true, get: function () { return rate_limit_1.checkRateLimit; } });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AAGrB,6CAA+C;AAAtC,6GAAA,cAAc,OAAA;AAGvB,iCAAgF;AAAvE,qGAAA,YAAY,OAAA;AAAE,iGAAA,QAAQ,OAAA;AAAE,iGAAA,QAAQ,OAAA;AAAE,4GAAA,mBAAmB,OAAA;AAE9D,iCAAgC;AAAvB,8FAAA,KAAK,OAAA;AAEd,2CAA8C;AAArC,4GAAA,cAAc,OAAA"}
@@ -0,0 +1,12 @@
1
+ import pino from "pino";
2
+ export interface CreateLoggerOptions {
3
+ level?: string;
4
+ name?: string;
5
+ }
6
+ /**
7
+ * Create a pino logger with sensible defaults:
8
+ * - Level from `LOG_LEVEL` env or "info"
9
+ * - Pretty-printing in non-production
10
+ */
11
+ export declare function createLogger(opts?: CreateLoggerOptions): pino.Logger;
12
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,CAAC,EAAE,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAWpE"}
package/dist/logger.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createLogger = createLogger;
7
+ const pino_1 = __importDefault(require("pino"));
8
+ /**
9
+ * Create a pino logger with sensible defaults:
10
+ * - Level from `LOG_LEVEL` env or "info"
11
+ * - Pretty-printing in non-production
12
+ */
13
+ function createLogger(opts) {
14
+ return (0, pino_1.default)({
15
+ level: opts?.level ?? process.env.LOG_LEVEL ?? "info",
16
+ ...(opts?.name && { name: opts.name }),
17
+ ...(process.env.NODE_ENV !== "production" && {
18
+ transport: {
19
+ target: "pino-pretty",
20
+ options: { colorize: true },
21
+ },
22
+ }),
23
+ });
24
+ }
25
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":";;;;;AAYA,oCAWC;AAvBD,gDAAwB;AAOxB;;;;GAIG;AACH,SAAgB,YAAY,CAAC,IAA0B;IACrD,OAAO,IAAA,cAAI,EAAC;QACV,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;QACrD,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;QACtC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI;YAC3C,SAAS,EAAE;gBACT,MAAM,EAAE,aAAa;gBACrB,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;aAC5B;SACF,CAAC;KACH,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface RateLimitResult {
2
+ allowed: boolean;
3
+ remaining: number;
4
+ resetAt: number;
5
+ }
6
+ /**
7
+ * In-memory sliding-window rate limiter.
8
+ *
9
+ * @param key - Unique identifier (e.g. `"login:${chatId}"`).
10
+ * @param max - Maximum attempts within the window.
11
+ * @param windowMs - Window duration in milliseconds.
12
+ */
13
+ export declare function checkRateLimit(key: string, max: number, windowMs: number): RateLimitResult;
14
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAgBD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,MAAM,GACf,eAAe,CAgBjB"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkRateLimit = checkRateLimit;
4
+ const store = new Map();
5
+ const CLEANUP_INTERVAL = 60_000;
6
+ let lastCleanup = Date.now();
7
+ function cleanup() {
8
+ const now = Date.now();
9
+ if (now - lastCleanup < CLEANUP_INTERVAL)
10
+ return;
11
+ lastCleanup = now;
12
+ for (const [key, entry] of store) {
13
+ if (entry.resetAt <= now)
14
+ store.delete(key);
15
+ }
16
+ }
17
+ /**
18
+ * In-memory sliding-window rate limiter.
19
+ *
20
+ * @param key - Unique identifier (e.g. `"login:${chatId}"`).
21
+ * @param max - Maximum attempts within the window.
22
+ * @param windowMs - Window duration in milliseconds.
23
+ */
24
+ function checkRateLimit(key, max, windowMs) {
25
+ cleanup();
26
+ const now = Date.now();
27
+ const entry = store.get(key);
28
+ if (!entry || entry.resetAt <= now) {
29
+ store.set(key, { count: 1, resetAt: now + windowMs });
30
+ return { allowed: true, remaining: max - 1, resetAt: now + windowMs };
31
+ }
32
+ if (entry.count >= max) {
33
+ return { allowed: false, remaining: 0, resetAt: entry.resetAt };
34
+ }
35
+ entry.count++;
36
+ return { allowed: true, remaining: max - entry.count, resetAt: entry.resetAt };
37
+ }
38
+ //# sourceMappingURL=rate-limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../src/rate-limit.ts"],"names":[],"mappings":";;AA2BA,wCAoBC;AAzCD,MAAM,KAAK,GAAG,IAAI,GAAG,EAA8C,CAAC;AAEpE,MAAM,gBAAgB,GAAG,MAAM,CAAC;AAChC,IAAI,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AAE7B,SAAS,OAAO;IACd,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,IAAI,GAAG,GAAG,WAAW,GAAG,gBAAgB;QAAE,OAAO;IACjD,WAAW,GAAG,GAAG,CAAC;IAClB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,OAAO,IAAI,GAAG;YAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,cAAc,CAC5B,GAAW,EACX,GAAW,EACX,QAAgB;IAEhB,OAAO,EAAE,CAAC;IACV,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAE7B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,IAAI,GAAG,EAAE,CAAC;QACnC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAC,CAAC;QACtD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,GAAG,QAAQ,EAAE,CAAC;IACxE,CAAC;IAED,IAAI,KAAK,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC;QACvB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,KAAK,EAAE,CAAC;IACd,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,KAAK,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;AACjF,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Promise-based delay.
3
+ */
4
+ export declare function sleep(ms: number): Promise<void>;
5
+ //# sourceMappingURL=sleep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.d.ts","sourceRoot":"","sources":["../src/sleep.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C"}
package/dist/sleep.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sleep = sleep;
4
+ /**
5
+ * Promise-based delay.
6
+ */
7
+ function sleep(ms) {
8
+ return new Promise((resolve) => setTimeout(resolve, ms));
9
+ }
10
+ //# sourceMappingURL=sleep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sleep.js","sourceRoot":"","sources":["../src/sleep.ts"],"names":[],"mappings":";;AAGA,sBAEC;AALD;;GAEG;AACH,SAAgB,KAAK,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@noambz/app-utils",
3
+ "version": "1.0.0",
4
+ "description": "Shared utilities: logger, fetchWithRetry, timezone helpers, sleep, rate limiter",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "require": "./dist/index.js",
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "utils",
23
+ "logger",
24
+ "fetch",
25
+ "timezone"
26
+ ],
27
+ "author": "noambz",
28
+ "license": "MIT",
29
+ "dependencies": {
30
+ "pino": "^10.3.1"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.0.0",
34
+ "pino-pretty": "^13.1.3",
35
+ "typescript": "^5.7.0"
36
+ }
37
+ }