@ya-accelerators/web-backend-framework 0.0.53 → 0.0.55

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/dist/index.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  export * from './app/index.ts';
2
2
  export * from './env.ts';
3
+ export * from './lock/index.ts';
3
4
  export * from './logger/index.ts';
4
5
  export * from './prisma/index.ts';
6
+ export * from './rate-limit/index.ts';
5
7
  export * from './redis/index.ts';
6
8
  export * from './s3/index.ts';
9
+ export * from './throttle/index.ts';
7
10
  export * from './util/index.ts';
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  export * from "./app/index.js";
2
2
  export * from "./env.js";
3
+ export * from "./lock/index.js";
3
4
  export * from "./logger/index.js";
4
5
  export * from "./prisma/index.js";
6
+ export * from "./rate-limit/index.js";
5
7
  export * from "./redis/index.js";
6
8
  export * from "./s3/index.js";
9
+ export * from "./throttle/index.js";
7
10
  export * from "./util/index.js";
8
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,UAAU,CAAA;AACxB,cAAc,iBAAiB,CAAA;AAC/B,cAAc,mBAAmB,CAAA;AACjC,cAAc,mBAAmB,CAAA;AACjC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kBAAkB,CAAA;AAChC,cAAc,eAAe,CAAA;AAC7B,cAAc,qBAAqB,CAAA;AACnC,cAAc,iBAAiB,CAAA"}
@@ -0,0 +1,27 @@
1
+ export declare const lock: {
2
+ acquire: ({ key, ttlSec }: {
3
+ key: string;
4
+ ttlSec: number;
5
+ }) => Promise<string | null>;
6
+ release: ({ key, token }: {
7
+ key: string;
8
+ token: string;
9
+ }) => Promise<void>;
10
+ /**
11
+ * Run `fn` while holding the lock. Returns `{ acquired: false }` without
12
+ * invoking `fn` if the lock could not be acquired. Always releases the lock,
13
+ * even when `fn` throws.
14
+ *
15
+ * The discriminated return lets callers tell "lock not acquired" from "fn
16
+ * ran and returned undefined" — a plain `T | undefined` would collide.
17
+ */
18
+ withLock: <T>({ key, ttlSec }: {
19
+ key: string;
20
+ ttlSec: number;
21
+ }, fn: () => Promise<T>) => Promise<{
22
+ acquired: true;
23
+ value: T;
24
+ } | {
25
+ acquired: false;
26
+ }>;
27
+ };
@@ -0,0 +1,62 @@
1
+ import { Script } from '@valkey/valkey-glide';
2
+ import { redis } from "../redis/index.js";
3
+ import { Util } from "../util/index.js";
4
+ /**
5
+ * Distributed mutex built on Redis SET NX + check-and-delete.
6
+ *
7
+ * Use to serialize work that must not run concurrently across processes —
8
+ * payout execution, batch jobs, anything where two concurrent runs would
9
+ * double-spend or corrupt state.
10
+ *
11
+ * `acquire` returns a unique token on success (null when the lock is held).
12
+ * `release` only deletes the key when the stored token still matches, so a
13
+ * TTL-expired-then-reacquired lock is never deleted by the original holder.
14
+ *
15
+ * Redis errors propagate as throws — callers decide whether to fail-closed
16
+ * via `.catch(() => null)` (acquire) or surface the failure.
17
+ *
18
+ * Note: even with the token check, a critical section that overruns `ttlSec`
19
+ * no longer holds the lock; another worker can enter while `fn` is still
20
+ * running. Pick `ttlSec` larger than the worst-case body duration.
21
+ */
22
+ const RELEASE_SCRIPT = new Script(`
23
+ if redis.call("GET", KEYS[1]) == ARGV[1] then
24
+ return redis.call("DEL", KEYS[1])
25
+ else
26
+ return 0
27
+ end
28
+ `);
29
+ export const lock = {
30
+ acquire: async ({ key, ttlSec }) => {
31
+ const token = Util.createULID();
32
+ const ok = await redis.setNx(key, token, ttlSec);
33
+ return ok != null ? token : null;
34
+ },
35
+ release: async ({ key, token }) => {
36
+ await redis.invokeScript(RELEASE_SCRIPT, { keys: [key], args: [token] });
37
+ },
38
+ /**
39
+ * Run `fn` while holding the lock. Returns `{ acquired: false }` without
40
+ * invoking `fn` if the lock could not be acquired. Always releases the lock,
41
+ * even when `fn` throws.
42
+ *
43
+ * The discriminated return lets callers tell "lock not acquired" from "fn
44
+ * ran and returned undefined" — a plain `T | undefined` would collide.
45
+ */
46
+ withLock: async ({ key, ttlSec }, fn) => {
47
+ const token = await lock.acquire({ key, ttlSec });
48
+ if (token === null) {
49
+ return { acquired: false };
50
+ }
51
+ try {
52
+ return { acquired: true, value: await fn() };
53
+ }
54
+ finally {
55
+ // Swallow release failures so a Redis blip in cleanup doesn't mask
56
+ // `fn`'s original exception. The framework-level redis.invokeScript
57
+ // already logs the failure, and the lock TTL guarantees recovery.
58
+ await lock.release({ key, token }).catch(() => undefined);
59
+ }
60
+ },
61
+ };
62
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lock/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAEvC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC;;;;;;CAMjC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG;IAClB,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,MAAM,EAAmC,EAA0B,EAAE;QAC1F,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,EAAE,CAAA;QAC/B,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAA;QAChD,OAAO,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAA;IAClC,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,KAAK,EAAkC,EAAiB,EAAE;QAC/E,MAAM,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IAC1E,CAAC;IACD;;;;;;;OAOG;IACH,QAAQ,EAAE,KAAK,EACb,EAAE,GAAG,EAAE,MAAM,EAAmC,EAChD,EAAoB,EACyC,EAAE;QAC/D,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC,CAAA;QACjD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;YACnB,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAA;QAC5B,CAAC;QACD,IAAI,CAAC;YACH,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAA;QAC9C,CAAC;gBAAS,CAAC;YACT,mEAAmE;YACnE,oEAAoE;YACpE,kEAAkE;YAClE,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QAC3D,CAAC;IACH,CAAC;CACF,CAAA"}
@@ -0,0 +1,16 @@
1
+ export declare const rateLimit: {
2
+ /**
3
+ * Increment the counter for `key` and decide whether the current attempt is
4
+ * allowed. Returns the post-increment count and how many attempts remain in
5
+ * the current window.
6
+ */
7
+ attempt: ({ key, max, windowSec, }: {
8
+ key: string;
9
+ max: number;
10
+ windowSec: number;
11
+ }) => Promise<{
12
+ allowed: boolean;
13
+ count: number;
14
+ remaining: number;
15
+ }>;
16
+ };
@@ -0,0 +1,38 @@
1
+ import { Script } from '@valkey/valkey-glide';
2
+ import { redis } from "../redis/index.js";
3
+ /**
4
+ * Fixed-window rate limiter built on a single atomic Lua script
5
+ * (INCR + EXPIRE on the first hit of a window).
6
+ *
7
+ * Use to cap the number of attempts against a key inside a rolling window —
8
+ * unauthenticated endpoints (password reset, signup, OTP request, etc.) that
9
+ * would otherwise be abusable by brute-force or spam.
10
+ *
11
+ * The INCR and EXPIRE run inside a Lua script, so the key is guaranteed to
12
+ * have a TTL whenever its value > 0 — an interrupt between the two ops is
13
+ * impossible.
14
+ *
15
+ * Redis errors propagate as throws — callers decide whether to fail-closed
16
+ * via `.catch(...)` or surface the failure.
17
+ */
18
+ const ATTEMPT_SCRIPT = new Script(`
19
+ local n = redis.call("INCR", KEYS[1])
20
+ if n == 1 then redis.call("EXPIRE", KEYS[1], ARGV[1]) end
21
+ return n
22
+ `);
23
+ export const rateLimit = {
24
+ /**
25
+ * Increment the counter for `key` and decide whether the current attempt is
26
+ * allowed. Returns the post-increment count and how many attempts remain in
27
+ * the current window.
28
+ */
29
+ attempt: async ({ key, max, windowSec, }) => {
30
+ const count = Number(await redis.invokeScript(ATTEMPT_SCRIPT, { keys: [key], args: [String(windowSec)] }));
31
+ return {
32
+ allowed: count <= max,
33
+ count,
34
+ remaining: Math.max(0, max - count),
35
+ };
36
+ },
37
+ };
38
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rate-limit/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAA;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEzC;;;;;;;;;;;;;;GAcG;AACH,MAAM,cAAc,GAAG,IAAI,MAAM,CAAC;;;;CAIjC,CAAC,CAAA;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG;IACvB;;;;OAIG;IACH,OAAO,EAAE,KAAK,EAAE,EACd,GAAG,EACH,GAAG,EACH,SAAS,GAKV,EAAmE,EAAE;QACpE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAC1G,OAAO;YACL,OAAO,EAAE,KAAK,IAAI,GAAG;YACrB,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,KAAK,CAAC;SACpC,CAAA;IACH,CAAC;CACF,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { type Script } from '@valkey/valkey-glide';
1
2
  export declare const redis: {
2
3
  health: () => Promise<"NG" | "OK">;
3
4
  set: (key: string, value: string, expirationSeconds?: number) => Promise<void>;
@@ -5,5 +6,10 @@ export declare const redis: {
5
6
  get: (key: string) => Promise<string | undefined>;
6
7
  remove: (key: string) => Promise<void>;
7
8
  incr: (key: string) => Promise<number>;
9
+ expire: (key: string, expirationSeconds: number) => Promise<boolean>;
8
10
  decr: (key: string) => Promise<number>;
11
+ invokeScript: (script: Script, opts: {
12
+ keys: string[];
13
+ args: string[];
14
+ }) => Promise<import("@valkey/valkey-glide").GlideReturnType>;
9
15
  };
@@ -84,6 +84,16 @@ export const redis = {
84
84
  throw e;
85
85
  }
86
86
  },
87
+ expire: async (key, expirationSeconds) => {
88
+ try {
89
+ const client = await getClient();
90
+ return await client.expire(key, expirationSeconds);
91
+ }
92
+ catch (e) {
93
+ logger.error('cache', { op: 'expire', key, error: e });
94
+ throw e;
95
+ }
96
+ },
87
97
  decr: async (key) => {
88
98
  try {
89
99
  const client = await getClient();
@@ -94,5 +104,15 @@ export const redis = {
94
104
  throw e;
95
105
  }
96
106
  },
107
+ invokeScript: async (script, opts) => {
108
+ try {
109
+ const client = await getClient();
110
+ return await client.invokeScript(script, opts);
111
+ }
112
+ catch (e) {
113
+ logger.error('cache', { op: 'invokeScript', key: opts.keys[0], error: e });
114
+ throw e;
115
+ }
116
+ },
97
117
  };
98
118
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/redis/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/redis/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * At-most-once-per-window throttle built on Redis SET NX.
3
+ *
4
+ * Use when an action should run at most once per `windowSec` — sanctions
5
+ * evaluation that should not re-fire on every call-log ingest, single-use
6
+ * token claims that must succeed exactly once across concurrent attempts.
7
+ *
8
+ * Unlike `lock`, there is no explicit release: the slot opens again only
9
+ * when the TTL expires.
10
+ *
11
+ * Redis errors propagate as throws — callers decide whether to fail-closed
12
+ * via `.catch(() => false)` or surface the failure.
13
+ */
14
+ export declare const throttle: {
15
+ tryAcquire: ({ key, windowSec }: {
16
+ key: string;
17
+ windowSec: number;
18
+ }) => Promise<boolean>;
19
+ };
@@ -0,0 +1,21 @@
1
+ import { redis } from "../redis/index.js";
2
+ /**
3
+ * At-most-once-per-window throttle built on Redis SET NX.
4
+ *
5
+ * Use when an action should run at most once per `windowSec` — sanctions
6
+ * evaluation that should not re-fire on every call-log ingest, single-use
7
+ * token claims that must succeed exactly once across concurrent attempts.
8
+ *
9
+ * Unlike `lock`, there is no explicit release: the slot opens again only
10
+ * when the TTL expires.
11
+ *
12
+ * Redis errors propagate as throws — callers decide whether to fail-closed
13
+ * via `.catch(() => false)` or surface the failure.
14
+ */
15
+ export const throttle = {
16
+ tryAcquire: async ({ key, windowSec }) => {
17
+ const ok = await redis.setNx(key, '1', windowSec);
18
+ return ok != null;
19
+ },
20
+ };
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/throttle/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAEzC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG;IACtB,UAAU,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,SAAS,EAAsC,EAAoB,EAAE;QAC7F,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,CAAC,CAAA;QACjD,OAAO,EAAE,IAAI,IAAI,CAAA;IACnB,CAAC;CACF,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ya-accelerators/web-backend-framework",
3
- "version": "0.0.53",
3
+ "version": "0.0.55",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "author": "YA Technologies OÜ",