valkyries 0.1.0 → 0.1.2

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.
@@ -15,3 +15,139 @@ Use `addAndCheck(ids, threshold)` for request paths. It atomically adds an event
15
15
  ```
16
16
 
17
17
  A threshold of `N` blocks the Nth request because the count is checked after incrementing.
18
+
19
+ ## Import
20
+
21
+ ```ts
22
+ import { RateLimiter } from "valkyries";
23
+ ```
24
+
25
+ or:
26
+
27
+ ```ts
28
+ import { RateLimiter } from "valkyries/rate-limiter";
29
+ ```
30
+
31
+ ## Constructor
32
+
33
+ ```ts
34
+ const limiter = new RateLimiter({
35
+ prefix: "login",
36
+ ttlSeconds: 60,
37
+ });
38
+ ```
39
+
40
+ ```ts
41
+ type RateLimiterOptions = {
42
+ prefix: string;
43
+ ttlSeconds: number;
44
+ client?: GlideClient;
45
+ };
46
+ ```
47
+
48
+ - `prefix`: namespace for this limiter.
49
+ - `ttlSeconds`: sliding-window size in seconds.
50
+ - `client`: optional `@valkey/valkey-glide` client. Defaults to the package rate-limiter client.
51
+
52
+ The limiter reads from the primary Valkey client to avoid replica lag in request-path checks.
53
+
54
+ ## `add(ids)`
55
+
56
+ ```ts
57
+ limiter.add(ids: string[]): Promise<void>
58
+ ```
59
+
60
+ Records events for all truthy IDs. Falsy IDs are ignored.
61
+
62
+ This method writes without checking a threshold. Use it when you want to count activity separately from request admission.
63
+
64
+ ## `addAndCheck(ids, threshold, ttlSeconds?)`
65
+
66
+ ```ts
67
+ limiter.addAndCheck(
68
+ ids: string[],
69
+ threshold: number,
70
+ ttlSeconds?: number,
71
+ ): Promise<{ counts: number[]; limited: boolean }>
72
+ ```
73
+
74
+ Atomically records events and returns the current counts.
75
+
76
+ - `counts` aligns with the filtered truthy IDs, not the original input.
77
+ - `limited` is `true` when any post-add count is greater than or equal to `threshold`.
78
+ - Empty or all-falsy input returns `{ counts: [], limited: false }`.
79
+ - `ttlSeconds` overrides the instance TTL for this check.
80
+ - Unexpected Valkey response types fail open with zero counts.
81
+
82
+ Example:
83
+
84
+ ```ts
85
+ const { counts, limited } = await limiter.addAndCheck(["ip:127.0.0.1"], 10);
86
+ ```
87
+
88
+ ## `isRateLimited(ids, threshold, ttlSeconds?)`
89
+
90
+ ```ts
91
+ limiter.isRateLimited(
92
+ ids: string[],
93
+ threshold: number,
94
+ ttlSeconds?: number,
95
+ ): Promise<boolean>
96
+ ```
97
+
98
+ Reads current counts without adding a new event. Returns `true` when any ID count is greater than or equal to `threshold` within the TTL window.
99
+
100
+ ## `get(ids, ttlSeconds?)`
101
+
102
+ ```ts
103
+ limiter.get(ids: string[], ttlSeconds?: number): Promise<number[]>
104
+ ```
105
+
106
+ Returns current counts for truthy IDs. Empty or all-falsy input returns `[]`.
107
+
108
+ The returned array aligns with the filtered truthy IDs.
109
+
110
+ ## `delete(...ids)`
111
+
112
+ ```ts
113
+ limiter.delete(...ids: string[]): Promise<number>
114
+ ```
115
+
116
+ Deletes limiter keys for truthy IDs and returns the Valkey unlink count.
117
+
118
+ ## `getKey(key)`
119
+
120
+ ```ts
121
+ limiter.getKey(key: string): string
122
+ ```
123
+
124
+ Returns the Valkey sorted-set key for an ID:
125
+
126
+ ```txt
127
+ rate-limiter:{prefix}:{id}
128
+ ```
129
+
130
+ ## `invalidate()`
131
+
132
+ ```ts
133
+ limiter.invalidate(): Promise<void>
134
+ ```
135
+
136
+ Deletes every limiter key for this instance prefix.
137
+
138
+ ## `RateLimiter.invalidate(prefix, client?)`
139
+
140
+ ```ts
141
+ RateLimiter.invalidate(prefix: string, client?: GlideClient): Promise<void>
142
+ ```
143
+
144
+ Deletes every limiter key for `prefix`. Passing an empty prefix invalidates all limiter namespaces visible to the client.
145
+
146
+ ## Events
147
+
148
+ The rate limiter emits:
149
+
150
+ - `rate-limiter:add` with `{ prefix, ids }`
151
+ - `rate-limiter:get` with `{ prefix, ids, counts }`
152
+ - `rate-limiter:delete` with `{ prefix, ids }`
153
+ - `rate-limiter:invalidate` with `{ prefix }`
@@ -0,0 +1,65 @@
1
+ # Utilities
2
+
3
+ These helpers are exported from the package root for applications that need the same normalization and serialization behavior as the built-in classes.
4
+
5
+ ## `normalizeKey(key)`
6
+
7
+ ```ts
8
+ normalizeKey(key: string): string
9
+ ```
10
+
11
+ Trims and lowercases cache keys.
12
+
13
+ ## `normalizeTtlResult(value)`
14
+
15
+ ```ts
16
+ normalizeTtlResult(value: unknown): number | null
17
+ ```
18
+
19
+ Normalizes Valkey millisecond TTL values to seconds. Preserves `-2` and `-1` sentinel values.
20
+
21
+ ## `normalizeCountResult(value)`
22
+
23
+ ```ts
24
+ normalizeCountResult(value: unknown): number
25
+ ```
26
+
27
+ Normalizes numeric or bigint Valkey counts. Returns `0` for unexpected values.
28
+
29
+ ## `ValkeyCacheTypeError`
30
+
31
+ ```ts
32
+ class ValkeyCacheTypeError extends TypeError
33
+ ```
34
+
35
+ Thrown when a cache value does not match the configured mode.
36
+
37
+ ## `serializeValue(value, mode)`
38
+
39
+ ```ts
40
+ serializeValue(
41
+ value: unknown,
42
+ mode: "json" | "text" | "buffer",
43
+ ): Promise<string | Buffer>
44
+ ```
45
+
46
+ Serializes values for cache storage and gzip-compresses payloads larger than 2 KiB.
47
+
48
+ ## `decodeValue(result, mode)`
49
+
50
+ ```ts
51
+ decodeValue(
52
+ result: GlideString | null,
53
+ mode: "json" | "text" | "buffer",
54
+ ): Promise<string | Buffer | Record<string, unknown> | null>
55
+ ```
56
+
57
+ Decodes and decompresses cache values. Returns `null` for Valkey misses.
58
+
59
+ ## `durationInMilliseconds(start)`
60
+
61
+ ```ts
62
+ durationInMilliseconds(start: bigint): number
63
+ ```
64
+
65
+ Converts a `process.hrtime.bigint()` start timestamp to elapsed milliseconds.
package/package.json CHANGED
@@ -1,9 +1,17 @@
1
1
  {
2
2
  "name": "valkyries",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Valkey utilities for caches, bloom filters, dynamic configuration, and rate limiting.",
5
+ "homepage": "https://github.com/jonathanong/valkyries#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/jonathanong/valkyries/issues"
8
+ },
5
9
  "license": "MIT",
6
10
  "author": "",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/jonathanong/valkyries"
14
+ },
7
15
  "files": [
8
16
  "dist",
9
17
  "README.md",
@@ -40,26 +48,13 @@
40
48
  "default": "./dist/rate-limiter.mjs"
41
49
  }
42
50
  },
43
- "scripts": {
44
- "build": "tsc --project tsconfig.build.json && pnpm run copy:scripts",
45
- "copy:scripts": "tsx build-scripts/copy-lua-scripts.mts",
46
- "typecheck": "tsc --noEmit --project tsconfig.json",
47
- "test": "vitest run",
48
- "test:coverage": "vitest run --coverage",
49
- "lint": "pnpm run oxlint && pnpm run oxfmt:check",
50
- "oxlint": "oxlint --type-aware --deny-warnings",
51
- "oxlint:fix": "oxlint --type-aware --fix --deny-warnings",
52
- "oxfmt": "oxfmt",
53
- "oxfmt:check": "oxfmt --check",
54
- "actionlint": "actionlint"
55
- },
56
51
  "dependencies": {
57
52
  "@valkey/valkey-glide": "^2.4.0"
58
53
  },
59
54
  "devDependencies": {
60
55
  "@types/node": "^25.8.0",
61
56
  "@vitest/coverage-v8": "^4.1.6",
62
- "actionlint": "^2.0.6",
57
+ "github-actionlint": "^1.7.12",
63
58
  "oxfmt": "^0.50.0",
64
59
  "oxlint": "^1.65.0",
65
60
  "oxlint-tsgolint": "^0.22.1",
@@ -70,11 +65,17 @@
70
65
  "engines": {
71
66
  "node": ">=24.0.0"
72
67
  },
73
- "packageManager": "pnpm@11.1.2",
74
- "pnpm": {
75
- "onlyBuiltDependencies": [
76
- "esbuild",
77
- "protobufjs"
78
- ]
68
+ "scripts": {
69
+ "build": "tsc --project tsconfig.build.json && pnpm run copy:scripts",
70
+ "copy:scripts": "tsx build-scripts/copy-lua-scripts.mts",
71
+ "typecheck": "tsc --noEmit --project tsconfig.json",
72
+ "test": "vitest run",
73
+ "test:coverage": "vitest run --coverage",
74
+ "lint": "pnpm run oxlint && pnpm run oxfmt:check",
75
+ "oxlint": "oxlint --type-aware --deny-warnings",
76
+ "oxlint:fix": "oxlint --type-aware --fix --deny-warnings",
77
+ "oxfmt": "oxfmt",
78
+ "oxfmt:check": "oxfmt --check",
79
+ "actionlint": "github-actionlint"
79
80
  }
80
- }
81
+ }