webcake-storefront-mcp 1.0.3 → 1.1.1

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,66 @@
1
+ /**
2
+ * Lazy, shared ioredis client used as a SHORT-TTL CACHE for OAuth access-token
3
+ * lookups (access_token → {jwt,wsid}). Returns null when no REDIS_URL is
4
+ * configured OR ioredis isn't installed — every caller then falls back to going
5
+ * directly to Postgres (or the in-memory map), so stdio/`npx` users and the
6
+ * offline `npm run smoke` gate keep working with ZERO infra.
7
+ *
8
+ * The cache is intentionally disposable: losing Redis (restart, eviction,
9
+ * expiry) just adds one Postgres round-trip per /mcp request — never a failure.
10
+ * We never block startup on the connection and tolerate command errors by
11
+ * degrading to the source-of-truth store on a per-call basis.
12
+ *
13
+ * ioredis is an OPTIONAL, CJS dependency (see package.json), so we require it via
14
+ * createRequire under ESM/Node16. `new Redis(url)` returns immediately and
15
+ * connects in the background; commands queue until the socket is up.
16
+ *
17
+ * Configure with REDIS_URL (or WEBCAKE_REDIS_URL), e.g. redis://default:pw@host:6379/0
18
+ */
19
+ import { createRequire } from "node:module";
20
+ const require = createRequire(import.meta.url);
21
+ let cached; // undefined = not yet resolved
22
+ function redactUrl(u) {
23
+ try {
24
+ const x = new URL(u);
25
+ if (x.password)
26
+ x.password = "***";
27
+ return x.toString();
28
+ }
29
+ catch {
30
+ return "redis";
31
+ }
32
+ }
33
+ /**
34
+ * Returns the shared Redis client, or null if Redis isn't configured/available.
35
+ * Memoized: resolves the connection (or its absence) exactly once per process.
36
+ */
37
+ export function getRedis() {
38
+ if (cached !== undefined)
39
+ return cached;
40
+ const url = process.env.REDIS_URL || process.env.WEBCAKE_REDIS_URL;
41
+ if (!url)
42
+ return (cached = null);
43
+ try {
44
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
45
+ const mod = require("ioredis");
46
+ const Redis = mod.default ?? mod;
47
+ const client = new Redis(url, {
48
+ maxRetriesPerRequest: 2,
49
+ enableOfflineQueue: true,
50
+ // Never let a connection blip crash the process — log and keep retrying.
51
+ retryStrategy: (times) => Math.min(times * 200, 3000),
52
+ });
53
+ client.on("error", (e) => console.error("[redis] error:", e?.message ?? e));
54
+ console.error(`[redis] OAuth token cache: ${redactUrl(url)}`);
55
+ cached = client;
56
+ }
57
+ catch (e) {
58
+ console.error("[redis] unavailable, using direct store lookups:", e?.message ?? e);
59
+ cached = null;
60
+ }
61
+ return cached;
62
+ }
63
+ /** True when a Redis cache backend is configured (used for log/diagnostics). */
64
+ export function redisEnabled() {
65
+ return getRedis() !== null;
66
+ }