@abinashpatri/cache 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.
- package/LICENSE +21 -0
- package/README.md +166 -0
- package/dist/index.d.mts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Abinash Patri
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @abinashpatri/cache
|
|
2
|
+
|
|
3
|
+
Production-ready Redis cache helper for Node.js and TypeScript.
|
|
4
|
+
|
|
5
|
+
Built for simple integration with:
|
|
6
|
+
- safe JSON serialization/deserialization
|
|
7
|
+
- TTL with jitter to reduce cache stampedes
|
|
8
|
+
- auto-cache wrapper (`remember`)
|
|
9
|
+
- ESM + CommonJS support
|
|
10
|
+
- full TypeScript typings
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @abinashpatri/cache
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- Node.js 18+
|
|
21
|
+
- A running Redis instance
|
|
22
|
+
|
|
23
|
+
Default Redis URL used by the library:
|
|
24
|
+
|
|
25
|
+
```txt
|
|
26
|
+
redis://127.0.0.1:6379
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { connect, remember, disconnect } from "@abinashpatri/cache";
|
|
33
|
+
|
|
34
|
+
async function main() {
|
|
35
|
+
await connect(process.env.REDIS_URL);
|
|
36
|
+
|
|
37
|
+
const user = await remember("user:42", 60, async () => {
|
|
38
|
+
// Replace with DB/API call
|
|
39
|
+
return { id: 42, name: "Abinash" };
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
console.log(user);
|
|
43
|
+
await disconnect();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
console.error(err);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## API
|
|
53
|
+
|
|
54
|
+
### `connect(url?: string): Promise<void>`
|
|
55
|
+
|
|
56
|
+
Initializes Redis connection safely.
|
|
57
|
+
|
|
58
|
+
- idempotent (safe to call multiple times)
|
|
59
|
+
- default URL is `redis://127.0.0.1:6379`
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
await connect();
|
|
63
|
+
// or
|
|
64
|
+
await connect("redis://localhost:6379");
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `disconnect(): Promise<void>`
|
|
68
|
+
|
|
69
|
+
Gracefully closes Redis connection.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
await disconnect();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### `ping(): Promise<string>`
|
|
76
|
+
|
|
77
|
+
Health check for Redis connection.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const status = await ping(); // "PONG"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `get<T = unknown>(key: string): Promise<T | null>`
|
|
84
|
+
|
|
85
|
+
Reads and parses JSON value from cache.
|
|
86
|
+
|
|
87
|
+
- returns `null` when key does not exist
|
|
88
|
+
- returns `null` when cached value is invalid JSON
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const profile = await get<{ id: number; name: string }>("user:42");
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### `set<T>(key: string, value: T, ttl = 60): Promise<void>`
|
|
95
|
+
|
|
96
|
+
Stores JSON value in cache with TTL (seconds).
|
|
97
|
+
|
|
98
|
+
- adds jitter (`0-9s`) to TTL internally to spread expirations
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
await set("user:42", { id: 42, name: "Abinash" }, 120);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `del(key: string): Promise<void>`
|
|
105
|
+
|
|
106
|
+
Deletes a cache key.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
await del("user:42");
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `remember<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T>`
|
|
113
|
+
|
|
114
|
+
Auto-cache helper:
|
|
115
|
+
1. returns cached value when available
|
|
116
|
+
2. otherwise executes `fn`
|
|
117
|
+
3. stores result in Redis with TTL
|
|
118
|
+
4. returns computed result
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const posts = await remember("posts:home", 30, async () => {
|
|
122
|
+
return fetchPostsFromDb();
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Import Styles
|
|
127
|
+
|
|
128
|
+
### Named imports (recommended)
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
import { connect, get, set, del, remember, ping, disconnect } from "@abinashpatri/cache";
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Default import
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import cache from "@abinashpatri/cache";
|
|
138
|
+
|
|
139
|
+
await cache.connect();
|
|
140
|
+
await cache.set("k", { ok: true }, 60);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Production Usage Notes
|
|
144
|
+
|
|
145
|
+
- Call `connect()` once during app startup.
|
|
146
|
+
- Reuse this single connection across your application.
|
|
147
|
+
- Always `await disconnect()` during graceful shutdown.
|
|
148
|
+
- Keep TTL short for rapidly changing data and longer for stable data.
|
|
149
|
+
- Use key namespacing (example: `service:entity:id`) to avoid collisions.
|
|
150
|
+
|
|
151
|
+
## Error Handling Pattern
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { connect } from "@abinashpatri/cache";
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
await connect(process.env.REDIS_URL);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
console.error("Failed to connect Redis", err);
|
|
160
|
+
// decide fallback strategy (disable cache / fail fast)
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare function get<T = unknown>(key: string): Promise<T | null>;
|
|
2
|
+
declare function set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
3
|
+
declare function del(key: string): Promise<void>;
|
|
4
|
+
declare function remember<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T>;
|
|
5
|
+
|
|
6
|
+
declare function connect(url?: string): Promise<void>;
|
|
7
|
+
declare function ping(): Promise<string>;
|
|
8
|
+
declare function disconnect(): Promise<void>;
|
|
9
|
+
|
|
10
|
+
declare const redis: {
|
|
11
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
12
|
+
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
13
|
+
del(key: string): Promise<void>;
|
|
14
|
+
remember<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T>;
|
|
15
|
+
connect: typeof connect;
|
|
16
|
+
disconnect: typeof disconnect;
|
|
17
|
+
ping: typeof ping;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { connect, redis as default, del, disconnect, get, ping, remember, set };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
declare function get<T = unknown>(key: string): Promise<T | null>;
|
|
2
|
+
declare function set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
3
|
+
declare function del(key: string): Promise<void>;
|
|
4
|
+
declare function remember<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T>;
|
|
5
|
+
|
|
6
|
+
declare function connect(url?: string): Promise<void>;
|
|
7
|
+
declare function ping(): Promise<string>;
|
|
8
|
+
declare function disconnect(): Promise<void>;
|
|
9
|
+
|
|
10
|
+
declare const redis: {
|
|
11
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
12
|
+
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
13
|
+
del(key: string): Promise<void>;
|
|
14
|
+
remember<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T>;
|
|
15
|
+
connect: typeof connect;
|
|
16
|
+
disconnect: typeof disconnect;
|
|
17
|
+
ping: typeof ping;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export { connect, redis as default, del, disconnect, get, ping, remember, set };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var f=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var v=Object.prototype.hasOwnProperty;var g=(n,t)=>{for(var e in t)f(n,e,{get:t[e],enumerable:!0})},b=(n,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let o of R(t))!v.call(n,o)&&o!==e&&f(n,o,{get:()=>t[o],enumerable:!(i=P(t,o))||i.enumerable});return n};var J=n=>b(f({},"__esModule",{value:!0}),n);var M={};g(M,{connect:()=>C,default:()=>h,del:()=>m,disconnect:()=>y,get:()=>s,ping:()=>w,remember:()=>d,set:()=>l});module.exports=J(M);var T=require("redis"),r=null;async function x(n="redis://127.0.0.1:6379"){return r||(r=(0,T.createClient)({url:n}),r.on("connect",()=>{console.log("Redis connected")}),r.on("error",t=>{console.error("Redis error:",t)})),r.isOpen||await r.connect(),r}function c(){if(!r)throw new Error("Redis not connected. Call connect() first.");return r}var p={};g(p,{del:()=>m,get:()=>s,remember:()=>d,set:()=>l});function O(n){try{return JSON.parse(n)}catch{return null}}async function s(n){let e=await c().get(n);return e?O(e):null}async function l(n,t,e=60){let i=c(),o=e+Math.floor(Math.random()*10);await i.set(n,JSON.stringify(t),{EX:o})}async function m(n){await c().del(n)}async function d(n,t,e){let i=await s(n);if(i!==null)return console.log("Cache hit:",n),i;console.log("Cache miss:",n);let o=await e();return await l(n,o,t),o}var u=!1,a=null;async function C(n){if(!u)return a||(a=x(n).then(()=>{u=!0})),a}async function w(){return c().ping()}async function y(){if(!u)return;await c().quit(),u=!1,a=null}var E={connect:C,disconnect:y,ping:w,...p},h=E;0&&(module.exports={connect,del,disconnect,get,ping,remember,set});
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/redis/connection.ts","../src/redis/cache.ts","../src/redis/index.ts"],"sourcesContent":["export * from \"./redis/index\";\nexport { default } from \"./redis/index\";\n","import { createClient, type RedisClientType } from \"redis\";\n\nlet client: RedisClientType | null = null;\n\nexport async function connect(url = \"redis://127.0.0.1:6379\"): Promise<RedisClientType> {\n if (!client) {\n client = createClient({ url });\n\n client.on(\"connect\", () => {\n console.log(\"Redis connected\");\n });\n\n client.on(\"error\", (err: unknown) => {\n console.error(\"Redis error:\", err);\n });\n }\n\n if (!client.isOpen) {\n await client.connect();\n }\n\n return client;\n}\n\nexport function getClient(): RedisClientType {\n if (!client) {\n throw new Error(\"Redis not connected. Call connect() first.\");\n }\n\n return client;\n}\n","import { getClient } from \"./connection\";\n\nfunction safeParse<T>(data: string): T | null {\n try {\n return JSON.parse(data) as T;\n } catch {\n return null;\n }\n}\n\nexport async function get<T = unknown>(key: string): Promise<T | null> {\n const client = getClient();\n const data = await client.get(key);\n\n return data ? safeParse<T>(data) : null;\n}\n\nexport async function set<T>(key: string, value: T, ttl = 60): Promise<void> {\n const client = getClient();\n const ttlWithJitter = ttl + Math.floor(Math.random() * 10);\n\n await client.set(key, JSON.stringify(value), {\n EX: ttlWithJitter,\n });\n}\n\nexport async function del(key: string): Promise<void> {\n const client = getClient();\n await client.del(key);\n}\n\nexport async function remember<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T> {\n const cached = await get<T>(key);\n\n if (cached !== null) {\n console.log(\"Cache hit:\", key);\n return cached;\n }\n\n console.log(\"Cache miss:\", key);\n\n const result = await fn();\n await set(key, result, ttl);\n\n return result;\n}\n","import { connect as connectClient, getClient } from \"./connection\";\nimport * as cache from \"./cache\";\n\nlet isConnected = false;\nlet connecting: Promise<void> | null = null;\n\nexport async function connect(url?: string): Promise<void> {\n if (isConnected) {\n return;\n }\n\n if (!connecting) {\n connecting = connectClient(url).then(() => {\n isConnected = true;\n });\n }\n\n return connecting;\n}\n\nexport async function ping(): Promise<string> {\n const client = getClient();\n return client.ping();\n}\n\nexport async function disconnect(): Promise<void> {\n if (!isConnected) {\n return;\n }\n\n const client = getClient();\n await client.quit();\n\n isConnected = false;\n connecting = null;\n}\n\nexport { get, set, del, remember } from \"./cache\";\n\nconst redis = {\n connect,\n disconnect,\n ping,\n ...cache,\n};\n\nexport default redis;\n"],"mappings":"yaAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,aAAAE,EAAA,YAAAC,EAAA,QAAAC,EAAA,eAAAC,EAAA,QAAAC,EAAA,SAAAC,EAAA,aAAAC,EAAA,QAAAC,IAAA,eAAAC,EAAAV,GCAA,IAAAW,EAAmD,iBAE/CC,EAAiC,KAErC,eAAsBC,EAAQC,EAAM,yBAAoD,CACtF,OAAKF,IACHA,KAAS,gBAAa,CAAE,IAAAE,CAAI,CAAC,EAE7BF,EAAO,GAAG,UAAW,IAAM,CACzB,QAAQ,IAAI,iBAAiB,CAC/B,CAAC,EAEDA,EAAO,GAAG,QAAUG,GAAiB,CACnC,QAAQ,MAAM,eAAgBA,CAAG,CACnC,CAAC,GAGEH,EAAO,QACV,MAAMA,EAAO,QAAQ,EAGhBA,CACT,CAEO,SAASI,GAA6B,CAC3C,GAAI,CAACJ,EACH,MAAM,IAAI,MAAM,4CAA4C,EAG9D,OAAOA,CACT,CC9BA,IAAAK,EAAA,GAAAC,EAAAD,EAAA,SAAAE,EAAA,QAAAC,EAAA,aAAAC,EAAA,QAAAC,IAEA,SAASC,EAAaC,EAAwB,CAC5C,GAAI,CACF,OAAO,KAAK,MAAMA,CAAI,CACxB,MAAQ,CACN,OAAO,IACT,CACF,CAEA,eAAsBC,EAAiBC,EAAgC,CAErE,IAAMF,EAAO,MADEG,EAAU,EACC,IAAID,CAAG,EAEjC,OAAOF,EAAOD,EAAaC,CAAI,EAAI,IACrC,CAEA,eAAsBI,EAAOF,EAAaG,EAAUC,EAAM,GAAmB,CAC3E,IAAMC,EAASJ,EAAU,EACnBK,EAAgBF,EAAM,KAAK,MAAM,KAAK,OAAO,EAAI,EAAE,EAEzD,MAAMC,EAAO,IAAIL,EAAK,KAAK,UAAUG,CAAK,EAAG,CAC3C,GAAIG,CACN,CAAC,CACH,CAEA,eAAsBC,EAAIP,EAA4B,CAEpD,MADeC,EAAU,EACZ,IAAID,CAAG,CACtB,CAEA,eAAsBQ,EAAYR,EAAaI,EAAaK,EAAkC,CAC5F,IAAMC,EAAS,MAAMX,EAAOC,CAAG,EAE/B,GAAIU,IAAW,KACb,eAAQ,IAAI,aAAcV,CAAG,EACtBU,EAGT,QAAQ,IAAI,cAAeV,CAAG,EAE9B,IAAMW,EAAS,MAAMF,EAAG,EACxB,aAAMP,EAAIF,EAAKW,EAAQP,CAAG,EAEnBO,CACT,CC1CA,IAAIC,EAAc,GACdC,EAAmC,KAEvC,eAAsBC,EAAQC,EAA6B,CACzD,GAAI,CAAAH,EAIJ,OAAKC,IACHA,EAAaC,EAAcC,CAAG,EAAE,KAAK,IAAM,CACzCH,EAAc,EAChB,CAAC,GAGIC,CACT,CAEA,eAAsBG,GAAwB,CAE5C,OADeC,EAAU,EACX,KAAK,CACrB,CAEA,eAAsBC,GAA4B,CAChD,GAAI,CAACN,EACH,OAIF,MADeK,EAAU,EACZ,KAAK,EAElBL,EAAc,GACdC,EAAa,IACf,CAIA,IAAMM,EAAQ,CACZ,QAAAL,EACA,WAAAI,EACA,KAAAF,EACA,GAAGI,CACL,EAEOC,EAAQF","names":["index_exports","__export","connect","redis_default","del","disconnect","get","ping","remember","set","__toCommonJS","import_redis","client","connect","url","err","getClient","cache_exports","__export","del","get","remember","set","safeParse","data","get","key","getClient","set","value","ttl","client","ttlWithJitter","del","remember","fn","cached","result","isConnected","connecting","connect","url","ping","getClient","disconnect","redis","cache_exports","redis_default"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var g=Object.defineProperty;var T=(n,t)=>{for(var e in t)g(n,e,{get:t[e],enumerable:!0})};import{createClient as x}from"redis";var o=null;async function m(n="redis://127.0.0.1:6379"){return o||(o=x({url:n}),o.on("connect",()=>{console.log("Redis connected")}),o.on("error",t=>{console.error("Redis error:",t)})),o.isOpen||await o.connect(),o}function r(){if(!o)throw new Error("Redis not connected. Call connect() first.");return o}var f={};T(f,{del:()=>d,get:()=>a,remember:()=>p,set:()=>u});function C(n){try{return JSON.parse(n)}catch{return null}}async function a(n){let e=await r().get(n);return e?C(e):null}async function u(n,t,e=60){let i=r(),c=e+Math.floor(Math.random()*10);await i.set(n,JSON.stringify(t),{EX:c})}async function d(n){await r().del(n)}async function p(n,t,e){let i=await a(n);if(i!==null)return console.log("Cache hit:",n),i;console.log("Cache miss:",n);let c=await e();return await u(n,c,t),c}var l=!1,s=null;async function w(n){if(!l)return s||(s=m(n).then(()=>{l=!0})),s}async function y(){return r().ping()}async function h(){if(!l)return;await r().quit(),l=!1,s=null}var P={connect:w,disconnect:h,ping:y,...f},R=P;export{w as connect,R as default,d as del,h as disconnect,a as get,y as ping,p as remember,u as set};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/redis/connection.ts","../src/redis/cache.ts","../src/redis/index.ts"],"sourcesContent":["import { createClient, type RedisClientType } from \"redis\";\n\nlet client: RedisClientType | null = null;\n\nexport async function connect(url = \"redis://127.0.0.1:6379\"): Promise<RedisClientType> {\n if (!client) {\n client = createClient({ url });\n\n client.on(\"connect\", () => {\n console.log(\"Redis connected\");\n });\n\n client.on(\"error\", (err: unknown) => {\n console.error(\"Redis error:\", err);\n });\n }\n\n if (!client.isOpen) {\n await client.connect();\n }\n\n return client;\n}\n\nexport function getClient(): RedisClientType {\n if (!client) {\n throw new Error(\"Redis not connected. Call connect() first.\");\n }\n\n return client;\n}\n","import { getClient } from \"./connection\";\n\nfunction safeParse<T>(data: string): T | null {\n try {\n return JSON.parse(data) as T;\n } catch {\n return null;\n }\n}\n\nexport async function get<T = unknown>(key: string): Promise<T | null> {\n const client = getClient();\n const data = await client.get(key);\n\n return data ? safeParse<T>(data) : null;\n}\n\nexport async function set<T>(key: string, value: T, ttl = 60): Promise<void> {\n const client = getClient();\n const ttlWithJitter = ttl + Math.floor(Math.random() * 10);\n\n await client.set(key, JSON.stringify(value), {\n EX: ttlWithJitter,\n });\n}\n\nexport async function del(key: string): Promise<void> {\n const client = getClient();\n await client.del(key);\n}\n\nexport async function remember<T>(key: string, ttl: number, fn: () => Promise<T>): Promise<T> {\n const cached = await get<T>(key);\n\n if (cached !== null) {\n console.log(\"Cache hit:\", key);\n return cached;\n }\n\n console.log(\"Cache miss:\", key);\n\n const result = await fn();\n await set(key, result, ttl);\n\n return result;\n}\n","import { connect as connectClient, getClient } from \"./connection\";\nimport * as cache from \"./cache\";\n\nlet isConnected = false;\nlet connecting: Promise<void> | null = null;\n\nexport async function connect(url?: string): Promise<void> {\n if (isConnected) {\n return;\n }\n\n if (!connecting) {\n connecting = connectClient(url).then(() => {\n isConnected = true;\n });\n }\n\n return connecting;\n}\n\nexport async function ping(): Promise<string> {\n const client = getClient();\n return client.ping();\n}\n\nexport async function disconnect(): Promise<void> {\n if (!isConnected) {\n return;\n }\n\n const client = getClient();\n await client.quit();\n\n isConnected = false;\n connecting = null;\n}\n\nexport { get, set, del, remember } from \"./cache\";\n\nconst redis = {\n connect,\n disconnect,\n ping,\n ...cache,\n};\n\nexport default redis;\n"],"mappings":"0FAAA,OAAS,gBAAAA,MAA0C,QAEnD,IAAIC,EAAiC,KAErC,eAAsBC,EAAQC,EAAM,yBAAoD,CACtF,OAAKF,IACHA,EAASD,EAAa,CAAE,IAAAG,CAAI,CAAC,EAE7BF,EAAO,GAAG,UAAW,IAAM,CACzB,QAAQ,IAAI,iBAAiB,CAC/B,CAAC,EAEDA,EAAO,GAAG,QAAUG,GAAiB,CACnC,QAAQ,MAAM,eAAgBA,CAAG,CACnC,CAAC,GAGEH,EAAO,QACV,MAAMA,EAAO,QAAQ,EAGhBA,CACT,CAEO,SAASI,GAA6B,CAC3C,GAAI,CAACJ,EACH,MAAM,IAAI,MAAM,4CAA4C,EAG9D,OAAOA,CACT,CC9BA,IAAAK,EAAA,GAAAC,EAAAD,EAAA,SAAAE,EAAA,QAAAC,EAAA,aAAAC,EAAA,QAAAC,IAEA,SAASC,EAAaC,EAAwB,CAC5C,GAAI,CACF,OAAO,KAAK,MAAMA,CAAI,CACxB,MAAQ,CACN,OAAO,IACT,CACF,CAEA,eAAsBC,EAAiBC,EAAgC,CAErE,IAAMF,EAAO,MADEG,EAAU,EACC,IAAID,CAAG,EAEjC,OAAOF,EAAOD,EAAaC,CAAI,EAAI,IACrC,CAEA,eAAsBI,EAAOF,EAAaG,EAAUC,EAAM,GAAmB,CAC3E,IAAMC,EAASJ,EAAU,EACnBK,EAAgBF,EAAM,KAAK,MAAM,KAAK,OAAO,EAAI,EAAE,EAEzD,MAAMC,EAAO,IAAIL,EAAK,KAAK,UAAUG,CAAK,EAAG,CAC3C,GAAIG,CACN,CAAC,CACH,CAEA,eAAsBC,EAAIP,EAA4B,CAEpD,MADeC,EAAU,EACZ,IAAID,CAAG,CACtB,CAEA,eAAsBQ,EAAYR,EAAaI,EAAaK,EAAkC,CAC5F,IAAMC,EAAS,MAAMX,EAAOC,CAAG,EAE/B,GAAIU,IAAW,KACb,eAAQ,IAAI,aAAcV,CAAG,EACtBU,EAGT,QAAQ,IAAI,cAAeV,CAAG,EAE9B,IAAMW,EAAS,MAAMF,EAAG,EACxB,aAAMP,EAAIF,EAAKW,EAAQP,CAAG,EAEnBO,CACT,CC1CA,IAAIC,EAAc,GACdC,EAAmC,KAEvC,eAAsBC,EAAQC,EAA6B,CACzD,GAAI,CAAAH,EAIJ,OAAKC,IACHA,EAAaC,EAAcC,CAAG,EAAE,KAAK,IAAM,CACzCH,EAAc,EAChB,CAAC,GAGIC,CACT,CAEA,eAAsBG,GAAwB,CAE5C,OADeC,EAAU,EACX,KAAK,CACrB,CAEA,eAAsBC,GAA4B,CAChD,GAAI,CAACN,EACH,OAIF,MADeK,EAAU,EACZ,KAAK,EAElBL,EAAc,GACdC,EAAa,IACf,CAIA,IAAMM,EAAQ,CACZ,QAAAL,EACA,WAAAI,EACA,KAAAF,EACA,GAAGI,CACL,EAEOC,EAAQF","names":["createClient","client","connect","url","err","getClient","cache_exports","__export","del","get","remember","set","safeParse","data","get","key","getClient","set","value","ttl","client","ttlWithJitter","del","remember","fn","cached","result","isConnected","connecting","connect","url","ping","getClient","disconnect","redis","cache_exports","redis_default"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@abinashpatri/cache",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production-grade cache & rate limit utility library",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"typecheck": "tsc --noEmit",
|
|
22
|
+
"prepublishOnly": "npm run build"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"utils",
|
|
26
|
+
"redis"
|
|
27
|
+
],
|
|
28
|
+
"author": "Abinash Patri",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"type": "commonjs",
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/express": "^5.0.6",
|
|
33
|
+
"@types/node": "^25.5.0",
|
|
34
|
+
"eslint": "^10.1.0",
|
|
35
|
+
"prettier": "^3.8.1",
|
|
36
|
+
"tsup": "^8.5.1",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"redis": "^5.11.0"
|
|
41
|
+
}
|
|
42
|
+
}
|