valkyries 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.
Files changed (164) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +120 -0
  3. package/dist/bloom-filter/batching.d.mts +3 -0
  4. package/dist/bloom-filter/batching.d.mts.map +1 -0
  5. package/dist/bloom-filter/batching.mjs +18 -0
  6. package/dist/bloom-filter/batching.mjs.map +1 -0
  7. package/dist/bloom-filter/lookups.d.mts +6 -0
  8. package/dist/bloom-filter/lookups.d.mts.map +1 -0
  9. package/dist/bloom-filter/lookups.mjs +101 -0
  10. package/dist/bloom-filter/lookups.mjs.map +1 -0
  11. package/dist/bloom-filter/rebuilds.d.mts +9 -0
  12. package/dist/bloom-filter/rebuilds.d.mts.map +1 -0
  13. package/dist/bloom-filter/rebuilds.mjs +66 -0
  14. package/dist/bloom-filter/rebuilds.mjs.map +1 -0
  15. package/dist/bloom-filter/results.d.mts +3 -0
  16. package/dist/bloom-filter/results.d.mts.map +1 -0
  17. package/dist/bloom-filter/results.mjs +23 -0
  18. package/dist/bloom-filter/results.mjs.map +1 -0
  19. package/dist/bloom-filter/scripts.d.mts +9 -0
  20. package/dist/bloom-filter/scripts.d.mts.map +1 -0
  21. package/dist/bloom-filter/scripts.mjs +10 -0
  22. package/dist/bloom-filter/scripts.mjs.map +1 -0
  23. package/dist/bloom-filter/types.d.mts +12 -0
  24. package/dist/bloom-filter/types.d.mts.map +1 -0
  25. package/dist/bloom-filter/types.mjs +2 -0
  26. package/dist/bloom-filter/types.mjs.map +1 -0
  27. package/dist/bloom-filter/writes.d.mts +5 -0
  28. package/dist/bloom-filter/writes.d.mts.map +1 -0
  29. package/dist/bloom-filter/writes.mjs +54 -0
  30. package/dist/bloom-filter/writes.mjs.map +1 -0
  31. package/dist/bloom-filter.d.mts +90 -0
  32. package/dist/bloom-filter.d.mts.map +1 -0
  33. package/dist/bloom-filter.mjs +154 -0
  34. package/dist/bloom-filter.mjs.map +1 -0
  35. package/dist/cache/batch-read.d.mts +7 -0
  36. package/dist/cache/batch-read.d.mts.map +1 -0
  37. package/dist/cache/batch-read.mjs +142 -0
  38. package/dist/cache/batch-read.mjs.map +1 -0
  39. package/dist/cache/constants.d.mts +7 -0
  40. package/dist/cache/constants.d.mts.map +1 -0
  41. package/dist/cache/constants.mjs +8 -0
  42. package/dist/cache/constants.mjs.map +1 -0
  43. package/dist/cache/core.d.mts +38 -0
  44. package/dist/cache/core.d.mts.map +1 -0
  45. package/dist/cache/core.mjs +171 -0
  46. package/dist/cache/core.mjs.map +1 -0
  47. package/dist/cache/deletes.d.mts +8 -0
  48. package/dist/cache/deletes.d.mts.map +1 -0
  49. package/dist/cache/deletes.mjs +44 -0
  50. package/dist/cache/deletes.mjs.map +1 -0
  51. package/dist/cache/mutations.d.mts +25 -0
  52. package/dist/cache/mutations.d.mts.map +1 -0
  53. package/dist/cache/mutations.mjs +181 -0
  54. package/dist/cache/mutations.mjs.map +1 -0
  55. package/dist/cache/set-if-not-invalidated-events.d.mts +4 -0
  56. package/dist/cache/set-if-not-invalidated-events.d.mts.map +1 -0
  57. package/dist/cache/set-if-not-invalidated-events.mjs +21 -0
  58. package/dist/cache/set-if-not-invalidated-events.mjs.map +1 -0
  59. package/dist/cache/single-read.d.mts +11 -0
  60. package/dist/cache/single-read.d.mts.map +1 -0
  61. package/dist/cache/single-read.mjs +73 -0
  62. package/dist/cache/single-read.mjs.map +1 -0
  63. package/dist/cache/stale-refresh.d.mts +15 -0
  64. package/dist/cache/stale-refresh.d.mts.map +1 -0
  65. package/dist/cache/stale-refresh.mjs +70 -0
  66. package/dist/cache/stale-refresh.mjs.map +1 -0
  67. package/dist/cache-metrics.d.mts +10 -0
  68. package/dist/cache-metrics.d.mts.map +1 -0
  69. package/dist/cache-metrics.mjs +12 -0
  70. package/dist/cache-metrics.mjs.map +1 -0
  71. package/dist/cache-utils.d.mts +20 -0
  72. package/dist/cache-utils.d.mts.map +1 -0
  73. package/dist/cache-utils.mjs +67 -0
  74. package/dist/cache-utils.mjs.map +1 -0
  75. package/dist/cache.d.mts +4 -0
  76. package/dist/cache.d.mts.map +1 -0
  77. package/dist/cache.mjs +4 -0
  78. package/dist/cache.mjs.map +1 -0
  79. package/dist/clients.d.mts +51 -0
  80. package/dist/clients.d.mts.map +1 -0
  81. package/dist/clients.mjs +95 -0
  82. package/dist/clients.mjs.map +1 -0
  83. package/dist/config.d.mts +6 -0
  84. package/dist/config.d.mts.map +1 -0
  85. package/dist/config.mjs +10 -0
  86. package/dist/config.mjs.map +1 -0
  87. package/dist/delete.d.mts +3 -0
  88. package/dist/delete.d.mts.map +1 -0
  89. package/dist/delete.mjs +23 -0
  90. package/dist/delete.mjs.map +1 -0
  91. package/dist/dynamic-config/fields.d.mts +6 -0
  92. package/dist/dynamic-config/fields.d.mts.map +1 -0
  93. package/dist/dynamic-config/fields.mjs +50 -0
  94. package/dist/dynamic-config/fields.mjs.map +1 -0
  95. package/dist/dynamic-config/storage.d.mts +33 -0
  96. package/dist/dynamic-config/storage.d.mts.map +1 -0
  97. package/dist/dynamic-config/storage.mjs +55 -0
  98. package/dist/dynamic-config/storage.mjs.map +1 -0
  99. package/dist/dynamic-config.d.mts +34 -0
  100. package/dist/dynamic-config.d.mts.map +1 -0
  101. package/dist/dynamic-config.mjs +173 -0
  102. package/dist/dynamic-config.mjs.map +1 -0
  103. package/dist/errors.d.mts +4 -0
  104. package/dist/errors.d.mts.map +1 -0
  105. package/dist/errors.mjs +15 -0
  106. package/dist/errors.mjs.map +1 -0
  107. package/dist/events.d.mts +84 -0
  108. package/dist/events.d.mts.map +1 -0
  109. package/dist/events.mjs +18 -0
  110. package/dist/events.mjs.map +1 -0
  111. package/dist/index.d.mts +14 -0
  112. package/dist/index.d.mts.map +1 -0
  113. package/dist/index.mjs +13 -0
  114. package/dist/index.mjs.map +1 -0
  115. package/dist/key-normalization.d.mts +2 -0
  116. package/dist/key-normalization.d.mts.map +1 -0
  117. package/dist/key-normalization.mjs +4 -0
  118. package/dist/key-normalization.mjs.map +1 -0
  119. package/dist/rate-limiter.d.mts +40 -0
  120. package/dist/rate-limiter.d.mts.map +1 -0
  121. package/dist/rate-limiter.mjs +123 -0
  122. package/dist/rate-limiter.mjs.map +1 -0
  123. package/dist/scripts/bloom-filter-add.lua +26 -0
  124. package/dist/scripts/bloom-filter-ensure-exists.lua +13 -0
  125. package/dist/scripts/bloom-filter-exists-if-ready.lua +14 -0
  126. package/dist/scripts/bloom-filter-exists.lua +14 -0
  127. package/dist/scripts/bloom-filter-mexists-if-ready.lua +19 -0
  128. package/dist/scripts/bloom-filter-mexists.lua +17 -0
  129. package/dist/scripts/bloom-filter-reserve.lua +11 -0
  130. package/dist/scripts/cache-delete-with-invalidation.lua +29 -0
  131. package/dist/scripts/cache-set-if-not-invalidated.lua +47 -0
  132. package/dist/scripts/dynamic-config-set-fields.lua +23 -0
  133. package/dist/scripts/get-value-with-ttl.lua +35 -0
  134. package/dist/scripts/get-values-with-ttl.lua +81 -0
  135. package/dist/scripts/rate-limiter-add-and-check.lua +34 -0
  136. package/dist/scripts/rate-limiter-add.lua +26 -0
  137. package/dist/scripts/rate-limiter-get.lua +22 -0
  138. package/dist/scripts.d.mts +4 -0
  139. package/dist/scripts.d.mts.map +1 -0
  140. package/dist/scripts.mjs +31 -0
  141. package/dist/scripts.mjs.map +1 -0
  142. package/dist/shutdown.d.mts +3 -0
  143. package/dist/shutdown.d.mts.map +1 -0
  144. package/dist/shutdown.mjs +27 -0
  145. package/dist/shutdown.mjs.map +1 -0
  146. package/dist/types.d.mts +76 -0
  147. package/dist/types.d.mts.map +1 -0
  148. package/dist/types.mjs +2 -0
  149. package/dist/types.mjs.map +1 -0
  150. package/dist/utils.d.mts +16 -0
  151. package/dist/utils.d.mts.map +1 -0
  152. package/dist/utils.mjs +35 -0
  153. package/dist/utils.mjs.map +1 -0
  154. package/docs/bloom-filters.md +12 -0
  155. package/docs/cache.md +19 -0
  156. package/docs/clients.md +13 -0
  157. package/docs/configuration.md +16 -0
  158. package/docs/dynamic-config.md +13 -0
  159. package/docs/events-and-metrics.md +15 -0
  160. package/docs/lua-scripts.md +7 -0
  161. package/docs/migration.md +74 -0
  162. package/docs/rate-limiter.md +17 -0
  163. package/docs/testing-and-ci.md +21 -0
  164. package/package.json +80 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamic-config.mjs","sourceRoot":"","sources":["../dynamic-config.mts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type ValkeyErrorHandler = (error: Error) => void;
2
+ export declare function setValkeyErrorHandler(handler: ValkeyErrorHandler): void;
3
+ export declare function handleValkeyError(error: unknown): void;
4
+ //# sourceMappingURL=errors.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.mts","sourceRoot":"","sources":["../errors.mts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;AASxD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAEvE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAGtD"}
@@ -0,0 +1,15 @@
1
+ /* v8 ignore next 4 -- default process-level fallback is replaced in tests and by applications. */
2
+ let valkeyErrorHandler = (error) => {
3
+ if (process.env.NODE_ENV === "test")
4
+ return;
5
+ // oxlint-disable-next-line no-console -- default OSS error handler
6
+ console.error(error);
7
+ };
8
+ export function setValkeyErrorHandler(handler) {
9
+ valkeyErrorHandler = handler;
10
+ }
11
+ export function handleValkeyError(error) {
12
+ const normalized = error instanceof Error ? error : new Error(String(error));
13
+ valkeyErrorHandler(normalized);
14
+ }
15
+ //# sourceMappingURL=errors.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.mjs","sourceRoot":"","sources":["../errors.mts"],"names":[],"mappings":"AAEA,kGAAkG;AAClG,IAAI,kBAAkB,GAAuB,CAAC,KAAK,EAAE,EAAE;IACrD,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM;QAAE,OAAO;IAC5C,mEAAmE;IACnE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,UAAU,qBAAqB,CAAC,OAA2B;IAC/D,kBAAkB,GAAG,OAAO,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,MAAM,UAAU,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7E,kBAAkB,CAAC,UAAU,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,84 @@
1
+ import { EventEmitter } from "node:events";
2
+ export interface ValkeyEventMap {
3
+ "cache:call": [
4
+ {
5
+ cacheName: string;
6
+ batch: boolean;
7
+ hits: number;
8
+ misses: number;
9
+ bloomMisses: number;
10
+ durationMs: number;
11
+ }
12
+ ];
13
+ "rate-limiter:add": [{
14
+ prefix: string;
15
+ ids: string[];
16
+ }];
17
+ "rate-limiter:get": [{
18
+ prefix: string;
19
+ ids: string[];
20
+ counts: number[];
21
+ }];
22
+ "rate-limiter:delete": [{
23
+ prefix: string;
24
+ ids: string[];
25
+ }];
26
+ "rate-limiter:invalidate": [{
27
+ prefix: string;
28
+ }];
29
+ "cache:hit": [{
30
+ cacheName: string;
31
+ keys: string[];
32
+ count: number;
33
+ }];
34
+ "cache:miss": [{
35
+ cacheName: string;
36
+ keys: string[];
37
+ count: number;
38
+ }];
39
+ "cache:bloom-miss": [{
40
+ cacheName: string;
41
+ keys: string[];
42
+ count: number;
43
+ }];
44
+ "cache:set": [{
45
+ cacheName: string;
46
+ keys: string[];
47
+ }];
48
+ "cache:set-skipped": [{
49
+ cacheName: string;
50
+ keys: string[];
51
+ }];
52
+ "cache:delete": [{
53
+ cacheName: string;
54
+ keys: string[];
55
+ }];
56
+ "cache:invalidate": [{
57
+ cacheName: string;
58
+ }];
59
+ "bloom-filter:exists": [{
60
+ name: string;
61
+ item: string;
62
+ result: boolean | null;
63
+ }];
64
+ "bloom-filter:mexists": [{
65
+ name: string;
66
+ items: string[];
67
+ results: (boolean | null)[];
68
+ }];
69
+ /**
70
+ * Emitted after each write to the filter:
71
+ * - `add()`: fires once with all items after all internal batches complete (aggregate event)
72
+ * - `addStream()` and `rebuildFromStream()`: fires once per batch chunk
73
+ * During a rebuild, items are written to a staging key and not yet visible in the live
74
+ * filter until the atomic rename completes — do not treat this event as confirmation of
75
+ * live filter membership.
76
+ */
77
+ "bloom-filter:add": [{
78
+ name: string;
79
+ items: string[];
80
+ }];
81
+ }
82
+ export declare const valkeyEvents: EventEmitter<ValkeyEventMap>;
83
+ export declare function emitValkeyEvent<K extends keyof ValkeyEventMap>(event: K, ...args: ValkeyEventMap[K]): void;
84
+ //# sourceMappingURL=events.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.mts","sourceRoot":"","sources":["../events.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE;QACZ;YACE,SAAS,EAAE,MAAM,CAAC;YAClB,KAAK,EAAE,OAAO,CAAC;YACf,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,WAAW,EAAE,MAAM,CAAC;YACpB,UAAU,EAAE,MAAM,CAAC;SACpB;KACF,CAAC;IACF,kBAAkB,EAAE,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC1E,qBAAqB,EAAE,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC3D,yBAAyB,EAAE,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,WAAW,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpE,YAAY,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,kBAAkB,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC3E,WAAW,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACrD,mBAAmB,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IAC7D,cAAc,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACxD,kBAAkB,EAAE,CAAC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,qBAAqB,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IAChF,sBAAsB,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;IACzF;;;;;;;OAOG;IACH,kBAAkB,EAAE,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACzD;AAED,eAAO,MAAM,YAAY,8BAAqC,CAAC;AAO/D,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,cAAc,EAC5D,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,QAQ3B"}
@@ -0,0 +1,18 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { handleValkeyError } from "./errors.mjs";
3
+ export const valkeyEvents = new EventEmitter();
4
+ /* v8 ignore next -- NODE_ENV is controlled by the test runner. */
5
+ if (process.env.NODE_ENV === "test") {
6
+ // Allow up to 1000 listeners — tests register multiple per-event handlers across parallel workers.
7
+ valkeyEvents.setMaxListeners(1000);
8
+ }
9
+ export function emitValkeyEvent(event, ...args) {
10
+ try {
11
+ // Cast needed: TypeScript can't resolve conditional types in EventEmitter.emit for generic K
12
+ valkeyEvents.emit(event, ...args);
13
+ }
14
+ catch (err) {
15
+ handleValkeyError(err);
16
+ }
17
+ }
18
+ //# sourceMappingURL=events.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.mjs","sourceRoot":"","sources":["../events.mts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAoCjD,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,YAAY,EAAkB,CAAC;AAC/D,kEAAkE;AAClE,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;IACpC,mGAAmG;IACnG,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAAQ,EACR,GAAG,IAAuB;IAE1B,IAAI,CAAC;QACH,6FAA6F;QAC7F,YAAY,CAAC,IAAI,CAAC,KAAY,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iBAAiB,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;AACH,CAAC"}
@@ -0,0 +1,14 @@
1
+ export * from "./events.mts";
2
+ export * from "./rate-limiter.mts";
3
+ export * from "./cache.mts";
4
+ export * from "./bloom-filter.mts";
5
+ export * from "./dynamic-config.mts";
6
+ export * from "./clients.mts";
7
+ export * from "./shutdown.mts";
8
+ export * from "./scripts.mts";
9
+ export * from "./utils.mts";
10
+ export * from "./errors.mts";
11
+ export * from "./key-normalization.mts";
12
+ export * from "./cache-metrics.mts";
13
+ export type { RateLimiterOptions, ValkeyBloomFilterOptions, ValkeyCacheOptions, ValkeyCacheMode, ValkeyCacheResponse, DynamicConfigField, DynamicConfigFieldType, DynamicConfigOptions, } from "./types.mts";
14
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","sourceRoot":"","sources":["../index.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC;AACpC,YAAY,EACV,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,aAAa,CAAC"}
package/dist/index.mjs ADDED
@@ -0,0 +1,13 @@
1
+ export * from "./events.mjs";
2
+ export * from "./rate-limiter.mjs";
3
+ export * from "./cache.mjs";
4
+ export * from "./bloom-filter.mjs";
5
+ export * from "./dynamic-config.mjs";
6
+ export * from "./clients.mjs";
7
+ export * from "./shutdown.mjs";
8
+ export * from "./scripts.mjs";
9
+ export * from "./utils.mjs";
10
+ export * from "./errors.mjs";
11
+ export * from "./key-normalization.mjs";
12
+ export * from "./cache-metrics.mjs";
13
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../index.mts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,yBAAyB,CAAC;AACxC,cAAc,qBAAqB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function normalizeKey(key: string): string;
2
+ //# sourceMappingURL=key-normalization.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-normalization.d.mts","sourceRoot":"","sources":["../key-normalization.mts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD"}
@@ -0,0 +1,4 @@
1
+ export function normalizeKey(key) {
2
+ return key.trim().toLowerCase();
3
+ }
4
+ //# sourceMappingURL=key-normalization.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-normalization.mjs","sourceRoot":"","sources":["../key-normalization.mts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAAC,GAAW;IACtC,OAAO,GAAG,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC"}
@@ -0,0 +1,40 @@
1
+ import type { RateLimiterOptions } from "./types.mts";
2
+ import type { GlideClient } from "@valkey/valkey-glide";
3
+ export declare class RateLimiter {
4
+ prefix: string;
5
+ ttl: number;
6
+ private client;
7
+ constructor({ prefix, ttlSeconds, client }: RateLimiterOptions);
8
+ add(ids: string[]): Promise<void>;
9
+ /**
10
+ * Atomically records this request and checks whether any ID exceeds the threshold.
11
+ * Returns `limited: true` when any post-add count >= threshold.
12
+ *
13
+ * NOTE: Unlike the old isRateLimited() + add() pattern (which only added on success),
14
+ * every call — including rejected ones — increments the counter. This means persistent
15
+ * abusers accumulate a larger count and their rate-limit window keeps refreshing,
16
+ * making the limiter more aggressive against sustained abuse.
17
+ *
18
+ * Falsy ids are silently filtered; `counts` aligns with the filtered ids, not the
19
+ * original array. Pre-filter ids before calling to maintain positional alignment.
20
+ */
21
+ addAndCheck(ids: string[], threshold: number, ttlSeconds?: number): Promise<{
22
+ counts: number[];
23
+ limited: boolean;
24
+ }>;
25
+ /**
26
+ * Returns true if any ID is at or above the threshold within the TTL window.
27
+ * Falsy ids are silently filtered (delegates to get()).
28
+ */
29
+ isRateLimited(ids: string[], threshold: number, ttlSeconds?: number): Promise<boolean>;
30
+ /**
31
+ * Returns the current request count for each ID within the TTL window.
32
+ * Falsy ids are silently filtered; returned counts align to filtered ids, not the input array.
33
+ */
34
+ get(ids: string[], ttlSeconds?: number): Promise<number[]>;
35
+ delete(...ids: string[]): Promise<number>;
36
+ getKey(key: string): string;
37
+ invalidate(): Promise<void>;
38
+ static invalidate(prefix: string, client?: GlideClient): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=rate-limiter.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.mts","sourceRoot":"","sources":["../rate-limiter.mts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAcxD,qBAAa,WAAW;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,MAAM,CAAc;gBAEhB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAgC,EAAE,EAAE,kBAAkB;IAMlF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE;IAUvB;;;;;;;;;;;OAWG;IACG,WAAW,CACf,GAAG,EAAE,MAAM,EAAE,EACb,SAAS,EAAE,MAAM,EACjB,UAAU,SAAW,GACpB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IA8BlD;;;OAGG;IACG,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,SAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAK9F;;;OAGG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,UAAU,SAAW,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAiB5D,MAAM,CAAC,GAAG,GAAG,EAAE,MAAM,EAAE;IAU7B,MAAM,CAAC,GAAG,EAAE,MAAM;IAIlB,UAAU;WAIG,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,cAA0B;CAQzE"}
@@ -0,0 +1,123 @@
1
+ import { rateLimiterValkeyClient } from "./clients.mjs";
2
+ import { loadScript, registerScript } from "./scripts.mjs";
3
+ import { deleteKeysWithPrefix } from "./delete.mjs";
4
+ import { emitValkeyEvent } from "./events.mjs";
5
+ import { normalizeCountResult } from "./utils.mjs";
6
+ import { handleValkeyError } from "./errors.mjs";
7
+ const NAMESPACE = "rate-limiter";
8
+ const rateLimiterAddScript = registerScript(loadScript("rate-limiter-add.lua", import.meta.url));
9
+ const rateLimiterGetScript = registerScript(loadScript("rate-limiter-get.lua", import.meta.url));
10
+ const rateLimiterAddAndCheckScript = registerScript(loadScript("rate-limiter-add-and-check.lua", import.meta.url));
11
+ export class RateLimiter {
12
+ prefix;
13
+ ttl;
14
+ client;
15
+ constructor({ prefix, ttlSeconds, client = rateLimiterValkeyClient }) {
16
+ this.prefix = prefix;
17
+ this.ttl = ttlSeconds;
18
+ this.client = client;
19
+ }
20
+ async add(ids) {
21
+ const filteredIds = ids.filter(Boolean);
22
+ if (filteredIds.length === 0)
23
+ return;
24
+ const keys = filteredIds.map((id) => this.getKey(id));
25
+ // Single script call for all keys (server time is used in script)
26
+ const args = [this.ttl.toString(), ...keys.map(() => Math.random().toString())];
27
+ await this.client.invokeScript(rateLimiterAddScript, { keys, args });
28
+ emitValkeyEvent("rate-limiter:add", { prefix: this.prefix, ids: filteredIds });
29
+ }
30
+ /**
31
+ * Atomically records this request and checks whether any ID exceeds the threshold.
32
+ * Returns `limited: true` when any post-add count >= threshold.
33
+ *
34
+ * NOTE: Unlike the old isRateLimited() + add() pattern (which only added on success),
35
+ * every call — including rejected ones — increments the counter. This means persistent
36
+ * abusers accumulate a larger count and their rate-limit window keeps refreshing,
37
+ * making the limiter more aggressive against sustained abuse.
38
+ *
39
+ * Falsy ids are silently filtered; `counts` aligns with the filtered ids, not the
40
+ * original array. Pre-filter ids before calling to maintain positional alignment.
41
+ */
42
+ async addAndCheck(ids, threshold, ttlSeconds = this.ttl) {
43
+ const filteredIds = ids.filter(Boolean);
44
+ if (filteredIds.length === 0)
45
+ return { counts: [], limited: false };
46
+ const keys = filteredIds.map((id) => this.getKey(id));
47
+ const args = [ttlSeconds.toString(), ...keys.map(() => Math.random().toString())];
48
+ const results = await this.client.invokeScript(rateLimiterAddAndCheckScript, {
49
+ keys,
50
+ args,
51
+ });
52
+ // Valkey should always return an array. If not, the Lua script likely ran (atomic scripts
53
+ // either complete or error), but the return type is unexpected so we can't trust it.
54
+ // Fail open (allow the request). Skip events since the Valkey response is unreliable;
55
+ // return zeros so callers get a length-aligned array.
56
+ // This error path requires mocking invokeScript to test directly.
57
+ /* v8 ignore next 5 -- malformed script return requires a mocked corrupted Valkey response. */
58
+ if (!Array.isArray(results)) {
59
+ handleValkeyError(new Error(`addAndCheck: unexpected Valkey response type ${typeof results}`));
60
+ return { counts: filteredIds.map(() => 0), limited: false };
61
+ }
62
+ const counts = results.map((result) => normalizeCountResult(result));
63
+ const limited = counts.some((count) => count >= threshold);
64
+ // Both events fire even when limited=true, since ZADD always runs for every call.
65
+ // (Events are suppressed only on the fail-open path above where counts are unreliable.)
66
+ emitValkeyEvent("rate-limiter:add", { prefix: this.prefix, ids: filteredIds });
67
+ emitValkeyEvent("rate-limiter:get", { prefix: this.prefix, ids: filteredIds, counts });
68
+ return { counts, limited };
69
+ }
70
+ /**
71
+ * Returns true if any ID is at or above the threshold within the TTL window.
72
+ * Falsy ids are silently filtered (delegates to get()).
73
+ */
74
+ async isRateLimited(ids, threshold, ttlSeconds = this.ttl) {
75
+ const results = await this.get(ids, ttlSeconds);
76
+ return results.some((result) => result >= threshold);
77
+ }
78
+ /**
79
+ * Returns the current request count for each ID within the TTL window.
80
+ * Falsy ids are silently filtered; returned counts align to filtered ids, not the input array.
81
+ */
82
+ async get(ids, ttlSeconds = this.ttl) {
83
+ const filteredIds = ids.filter(Boolean);
84
+ if (filteredIds.length === 0)
85
+ return [];
86
+ const keys = filteredIds.map((id) => this.getKey(id));
87
+ // Single read-only script call for all keys (server time is used in script)
88
+ // Rate limiter checks need primary reads to avoid replica lag causing stale counts.
89
+ const results = await this.client.invokeScript(rateLimiterGetScript, {
90
+ keys,
91
+ args: [ttlSeconds.toString()],
92
+ });
93
+ // Results are an array of numbers from ZCOUNT; return zeros on unexpected type (fail open)
94
+ if (!Array.isArray(results))
95
+ return filteredIds.map(() => 0);
96
+ const counts = results.map((result) => normalizeCountResult(result));
97
+ emitValkeyEvent("rate-limiter:get", { prefix: this.prefix, ids: filteredIds, counts });
98
+ return counts;
99
+ }
100
+ async delete(...ids) {
101
+ if (ids.length === 0)
102
+ return 0;
103
+ const filteredIds = ids.filter(Boolean);
104
+ if (filteredIds.length === 0)
105
+ return 0;
106
+ const keys = filteredIds.map((id) => this.getKey(id));
107
+ const count = await this.client.unlink(keys);
108
+ emitValkeyEvent("rate-limiter:delete", { prefix: this.prefix, ids: filteredIds });
109
+ return count;
110
+ }
111
+ getKey(key) {
112
+ return `${NAMESPACE}:${this.prefix}:{${key}}`;
113
+ }
114
+ invalidate() {
115
+ return RateLimiter.invalidate(this.prefix, this.client);
116
+ }
117
+ static async invalidate(prefix, client = rateLimiterValkeyClient) {
118
+ const result = await deleteKeysWithPrefix(client, prefix ? `${NAMESPACE}:${prefix}:*` : `${NAMESPACE}:*`);
119
+ emitValkeyEvent("rate-limiter:invalidate", { prefix });
120
+ return result;
121
+ }
122
+ }
123
+ //# sourceMappingURL=rate-limiter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.mjs","sourceRoot":"","sources":["../rate-limiter.mts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ -- Add items to the live bloom filter and, if an active rebuild is running,
2
+ -- also to the building key. No-op if neither key exists (post-#1955) so a
3
+ -- missing filter stays missing and cache reads fall back to the DB rather
4
+ -- than to an empty / under-provisioned auto-created filter.
5
+ -- This script is write-only and must be executed using the write client.
6
+ -- KEYS[1] = live key
7
+ -- KEYS[2] = building key
8
+ -- ARGV[1..n] = items to add
9
+ -- Returns: 1 if any write occurred, 0 otherwise.
10
+ if #ARGV == 0 then
11
+ return 0
12
+ end
13
+
14
+ local wrote = 0
15
+
16
+ if redis.call('EXISTS', KEYS[1]) == 1 then
17
+ redis.call('BF.MADD', KEYS[1], unpack(ARGV))
18
+ wrote = 1
19
+ end
20
+
21
+ if redis.call('EXISTS', KEYS[2]) == 1 then
22
+ redis.call('BF.MADD', KEYS[2], unpack(ARGV))
23
+ wrote = 1
24
+ end
25
+
26
+ return wrote
@@ -0,0 +1,13 @@
1
+ -- Conditionally create a bloom filter only if the key does not already exist.
2
+ -- Unlike calling BF.RESERVE directly, this avoids a server-side "item exists"
3
+ -- error (which the Valkey Glide logger surfaces as a WARN even when caught in JS).
4
+ -- KEYS[1] = bloom filter key
5
+ -- ARGV[1] = error_rate (e.g. "0.01")
6
+ -- ARGV[2] = capacity (integer as string)
7
+ -- ARGV[3] = expansion rate (integer as string)
8
+ local key = KEYS[1]
9
+ if redis.call('EXISTS', key) == 1 then
10
+ return 0
11
+ end
12
+ redis.call('BF.RESERVE', key, ARGV[1], ARGV[2], 'EXPANSION', ARGV[3])
13
+ return 1
@@ -0,0 +1,14 @@
1
+ -- Check a ready marker, bloom filter existence, and one item in one roundtrip.
2
+ -- KEYS[1] = ready marker key
3
+ -- KEYS[2] = bloom filter key
4
+ -- ARGV[1] = item
5
+ -- Returns: 1 if item may exist, 0 if definitely not present, -1 if not ready/unavailable.
6
+ if redis.call('EXISTS', KEYS[1]) == 0 then
7
+ return -1
8
+ end
9
+
10
+ if redis.call('EXISTS', KEYS[2]) == 0 then
11
+ return -1
12
+ end
13
+
14
+ return redis.call('BF.EXISTS', KEYS[2], ARGV[1])
@@ -0,0 +1,14 @@
1
+ -- Check bloom filter existence in a single roundtrip
2
+ -- Read-only: execute with cacheValkeyClient
3
+ -- KEYS[1]: filter key
4
+ -- ARGV[1]: item to check
5
+ -- Returns: 1 if item may exist, 0 if definitely not present, -1 if filter doesn't exist
6
+
7
+ local key = KEYS[1]
8
+ local item = ARGV[1]
9
+
10
+ if redis.call('EXISTS', key) == 0 then
11
+ return -1
12
+ end
13
+
14
+ return redis.call('BF.EXISTS', key, item)
@@ -0,0 +1,19 @@
1
+ -- Check a ready marker, bloom filter existence, and multiple items in one roundtrip.
2
+ -- KEYS[1] = ready marker key
3
+ -- KEYS[2] = bloom filter key
4
+ -- ARGV[1..n] = items
5
+ -- Returns: array where 1=may exist, 0=definitely not present, -1=not ready/unavailable.
6
+ local result = {}
7
+
8
+ if #ARGV == 0 then
9
+ return result
10
+ end
11
+
12
+ if redis.call('EXISTS', KEYS[1]) == 0 or redis.call('EXISTS', KEYS[2]) == 0 then
13
+ for i = 1, #ARGV do
14
+ result[i] = -1
15
+ end
16
+ return result
17
+ end
18
+
19
+ return redis.call('BF.MEXISTS', KEYS[2], unpack(ARGV))
@@ -0,0 +1,17 @@
1
+ -- Check multiple items in bloom filter in a single roundtrip
2
+ -- Read-only: execute with cacheValkeyClient
3
+ -- KEYS[1]: filter key
4
+ -- ARGV[1..n]: items to check
5
+ -- Returns: array where 1=may exist, 0=definitely not present, -1=filter doesn't exist
6
+
7
+ local key = KEYS[1]
8
+
9
+ if redis.call('EXISTS', key) == 0 then
10
+ local result = {}
11
+ for i = 1, #ARGV do
12
+ result[i] = -1
13
+ end
14
+ return result
15
+ end
16
+
17
+ return redis.call('BF.MEXISTS', key, unpack(ARGV))
@@ -0,0 +1,11 @@
1
+ -- Atomically unlink and re-create a bloom filter key.
2
+ -- Combining UNLINK + BF.RESERVE in a single Lua script prevents a race condition
3
+ -- where two concurrent callers both call UNLINK and then both call BF.RESERVE,
4
+ -- causing the second caller to receive an "item exists" error.
5
+ -- This script is write-only and must be executed using the write client.
6
+ -- KEYS[1] = bloom filter key
7
+ -- ARGV[1] = error_rate (e.g. "0.01")
8
+ -- ARGV[2] = capacity (integer as string)
9
+ -- ARGV[3] = expansion rate (integer as string)
10
+ redis.call('UNLINK', KEYS[1])
11
+ return redis.call('BF.RESERVE', KEYS[1], ARGV[1], ARGV[2], 'EXPANSION', ARGV[3])
@@ -0,0 +1,29 @@
1
+ -- Atomically delete cache keys and record short-lived invalidation timestamps.
2
+ -- KEYS[1..n] = cache keys
3
+ -- KEYS[n+1..2n] = invalidation marker keys matching each cache key
4
+ -- ARGV[1] = number of cache keys
5
+ -- ARGV[2] = invalidation marker TTL in seconds
6
+ if #KEYS == 0 then
7
+ return 0
8
+ end
9
+
10
+ local keyCount = tonumber(ARGV[1])
11
+ local invalidationTtl = tonumber(ARGV[2])
12
+ if keyCount == nil or invalidationTtl == nil then
13
+ error('cache-delete-with-invalidation: key count and invalidation TTL are required')
14
+ end
15
+ if keyCount == 0 then
16
+ return 0
17
+ end
18
+ if #KEYS ~= keyCount * 2 then
19
+ error('cache-delete-with-invalidation: expected cache keys followed by invalidation keys')
20
+ end
21
+
22
+ local cacheKeys = {}
23
+ for i = 1, keyCount do
24
+ cacheKeys[i] = KEYS[i]
25
+ local invalidationKey = KEYS[keyCount + i]
26
+ redis.call('SET', invalidationKey, '1', 'EX', invalidationTtl)
27
+ end
28
+
29
+ return redis.call('UNLINK', unpack(cacheKeys))
@@ -0,0 +1,47 @@
1
+ -- Conditionally set cache values unless a newer invalidation marker exists.
2
+ -- KEYS[1..n] = cache keys
3
+ -- KEYS[n+1..2n] = invalidation marker keys matching each cache key
4
+ -- ARGV[1] = number of cache keys
5
+ -- Repeated per key:
6
+ -- ttl seconds
7
+ -- serialized value
8
+ -- Returns one result per key: 1 when set, 0 when skipped.
9
+ if #KEYS == 0 then
10
+ return {}
11
+ end
12
+
13
+ local keyCount = tonumber(ARGV[1])
14
+ if keyCount == nil then
15
+ error('cache-set-if-not-invalidated: key count is required')
16
+ end
17
+ if keyCount == 0 then
18
+ return {}
19
+ end
20
+ if #KEYS ~= keyCount * 2 then
21
+ error('cache-set-if-not-invalidated: expected cache keys followed by invalidation keys')
22
+ end
23
+ if #ARGV ~= 1 + (keyCount * 2) then
24
+ error('cache-set-if-not-invalidated: expected ttl and value for each key')
25
+ end
26
+
27
+ local results = {}
28
+ for i = 1, keyCount do
29
+ local cacheKey = KEYS[i]
30
+ local invalidationKey = KEYS[keyCount + i]
31
+ local argOffset = 1 + ((i - 1) * 2)
32
+ local ttl = tonumber(ARGV[argOffset + 1])
33
+ local value = ARGV[argOffset + 2]
34
+
35
+ if ttl == nil then
36
+ error('cache-set-if-not-invalidated: ttl must be a number')
37
+ end
38
+
39
+ if redis.call('EXISTS', invalidationKey) == 1 then
40
+ table.insert(results, 0)
41
+ else
42
+ redis.call('SET', cacheKey, value, 'EX', ttl)
43
+ table.insert(results, 1)
44
+ end
45
+ end
46
+
47
+ return results
@@ -0,0 +1,23 @@
1
+ -- Dynamic config set-fields operation (HSET + PUBLISH all in one roundtrip)
2
+ -- KEYS[1]: hash key
3
+ -- ARGV[1,3,5,...]: field names
4
+ -- ARGV[2,4,6,...]: field values
5
+ -- Sets all fields in the hash and publishes per-field update events
6
+
7
+ -- Guard: HSET with no fields is a Valkey error
8
+ if #ARGV == 0 then return 0 end
9
+ -- Guard: ARGV must contain field/value pairs (even length)
10
+ assert(#ARGV % 2 == 0, 'ARGV must contain field/value pairs (even length)')
11
+
12
+ -- HSET all fields at once
13
+ redis.call('HSET', KEYS[1], unpack(ARGV))
14
+
15
+ -- PUBLISH per field so subscribers get real-time updates
16
+ local numArgs = #ARGV
17
+ for i = 1, numArgs, 2 do
18
+ local field = ARGV[i]
19
+ local value = ARGV[i + 1]
20
+ redis.call('PUBLISH', KEYS[1] .. ':' .. field, value)
21
+ end
22
+
23
+ return 1