@simpleapps-com/augur-server 0.1.9 → 0.2.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/dist/index.d.ts +1 -50
- package/dist/index.js +0 -159
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -11,55 +11,6 @@ declare function cacheSet(key: string, value: string, ttlSeconds: number): Promi
|
|
|
11
11
|
declare function getCircuitState(): "closed" | "open";
|
|
12
12
|
declare function isRedisConnected(): boolean;
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
* Wraps an SDK call with Redis caching.
|
|
16
|
-
*
|
|
17
|
-
* @param prefix - Redis key prefix (e.g. "mysite:")
|
|
18
|
-
* @param methodPath - Dot-separated SDK method path (used as cache key)
|
|
19
|
-
* @param method - The SDK method to call
|
|
20
|
-
* @param args - Arguments to pass to the SDK method. If any argument
|
|
21
|
-
* contains an `edgeCache` property, the result is cached in Redis for
|
|
22
|
-
* that duration. Without `edgeCache`, the call bypasses cache.
|
|
23
|
-
*
|
|
24
|
-
* Accepted `edgeCache` values (matching augur-api CacheParams):
|
|
25
|
-
* - Numbers: 1–8 (hours)
|
|
26
|
-
* - Sub-hour strings: '30s', '1m', '5m'
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```ts
|
|
30
|
-
* const result = await cachedSdkCall(
|
|
31
|
-
* "mysite:",
|
|
32
|
-
* "items.categories.get",
|
|
33
|
-
* augurServices.items.categories.get,
|
|
34
|
-
* itemCategoryUid,
|
|
35
|
-
* { edgeCache: 1 },
|
|
36
|
-
* );
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
declare function cachedSdkCall<TArgs extends unknown[], TResult>(prefix: string, methodPath: string, method: (...args: TArgs) => Promise<TResult>, ...args: TArgs): Promise<TResult>;
|
|
40
|
-
declare function cachedSdkCall(prefix: string, methodPath: string, method: (...args: any[]) => Promise<any>, ...args: any[]): Promise<any>;
|
|
41
|
-
/** Returns aggregated cache stats for monitoring endpoints. */
|
|
42
|
-
declare function getCacheStats(): {
|
|
43
|
-
stats: {
|
|
44
|
-
hits: number;
|
|
45
|
-
misses: number;
|
|
46
|
-
errors: number;
|
|
47
|
-
hitRate: string;
|
|
48
|
-
};
|
|
49
|
-
topKeys: {
|
|
50
|
-
key: string;
|
|
51
|
-
hits: number;
|
|
52
|
-
misses: number;
|
|
53
|
-
avgMs: number;
|
|
54
|
-
}[];
|
|
55
|
-
recentOps: {
|
|
56
|
-
op: "HIT" | "MISS" | "SKIP" | "ERROR";
|
|
57
|
-
key: string;
|
|
58
|
-
ms: number;
|
|
59
|
-
ago: string;
|
|
60
|
-
}[];
|
|
61
|
-
};
|
|
62
|
-
|
|
63
14
|
/**
|
|
64
15
|
* Calls an Augur SDK method, forwarding all arguments with full type safety.
|
|
65
16
|
*
|
|
@@ -96,4 +47,4 @@ declare function createServerQueryClient(): QueryClient;
|
|
|
96
47
|
*/
|
|
97
48
|
declare const getServerQueryClient: () => QueryClient;
|
|
98
49
|
|
|
99
|
-
export { cacheGet, cacheSet,
|
|
50
|
+
export { cacheGet, cacheSet, createServerQueryClient, env, getCircuitState, getServerQueryClient, isDev, isProduction, isRedisConnected, isStaging, sdkCall };
|
package/dist/index.js
CHANGED
|
@@ -132,163 +132,6 @@ function isRedisConnected() {
|
|
|
132
132
|
return state.client?.status === "ready";
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
// src/cache/sdk-cache.ts
|
|
136
|
-
import crypto from "crypto";
|
|
137
|
-
var CACHE_ENABLED = process.env.REDIS_CACHE_ENABLED !== "false";
|
|
138
|
-
var debugEnabled2 = isDev || isStaging;
|
|
139
|
-
var g2 = globalThis;
|
|
140
|
-
if (!g2.__sdkCacheState) {
|
|
141
|
-
g2.__sdkCacheState = { statsMap: /* @__PURE__ */ new Map(), recentOps: [] };
|
|
142
|
-
}
|
|
143
|
-
var { statsMap, recentOps } = g2.__sdkCacheState;
|
|
144
|
-
var MAX_RECENT = 50;
|
|
145
|
-
function pushRecent(op) {
|
|
146
|
-
recentOps.push(op);
|
|
147
|
-
if (recentOps.length > MAX_RECENT) recentOps.shift();
|
|
148
|
-
}
|
|
149
|
-
function getOrCreateStats(method) {
|
|
150
|
-
let s = statsMap.get(method);
|
|
151
|
-
if (!s) {
|
|
152
|
-
s = { hits: 0, misses: 0, errors: 0, totalMs: 0 };
|
|
153
|
-
statsMap.set(method, s);
|
|
154
|
-
}
|
|
155
|
-
return s;
|
|
156
|
-
}
|
|
157
|
-
function hashArgs(args) {
|
|
158
|
-
const normalized = JSON.stringify(args, (_, v) => {
|
|
159
|
-
if (v && typeof v === "object" && !Array.isArray(v)) {
|
|
160
|
-
return Object.keys(v).sort().reduce(
|
|
161
|
-
(acc, k) => {
|
|
162
|
-
acc[k] = v[k];
|
|
163
|
-
return acc;
|
|
164
|
-
},
|
|
165
|
-
{}
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
return v;
|
|
169
|
-
});
|
|
170
|
-
return crypto.createHash("sha256").update(normalized).digest("hex").slice(0, 8);
|
|
171
|
-
}
|
|
172
|
-
function extractTtlSeconds(args) {
|
|
173
|
-
for (let i = args.length - 1; i >= 0; i--) {
|
|
174
|
-
const arg = args[i];
|
|
175
|
-
if (arg && typeof arg === "object" && "edgeCache" in arg) {
|
|
176
|
-
const val = arg.edgeCache;
|
|
177
|
-
if (typeof val === "number") return val * 3600;
|
|
178
|
-
if (typeof val === "string") {
|
|
179
|
-
if (val.endsWith("s")) return parseInt(val, 10);
|
|
180
|
-
if (val.endsWith("m")) return parseInt(val, 10) * 60;
|
|
181
|
-
const n = Number(val);
|
|
182
|
-
return isNaN(n) ? void 0 : n * 3600;
|
|
183
|
-
}
|
|
184
|
-
return void 0;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
return void 0;
|
|
188
|
-
}
|
|
189
|
-
function formatTtl(seconds) {
|
|
190
|
-
if (seconds < 60) return `${seconds}s`;
|
|
191
|
-
if (seconds < 3600) return `${Math.round(seconds / 60)}m`;
|
|
192
|
-
return `${Math.round(seconds / 3600)}h`;
|
|
193
|
-
}
|
|
194
|
-
async function cachedSdkCall(prefix, methodPath, method, ...args) {
|
|
195
|
-
const ttlSeconds = extractTtlSeconds(args);
|
|
196
|
-
if (!CACHE_ENABLED || ttlSeconds === void 0) {
|
|
197
|
-
if (debugEnabled2) {
|
|
198
|
-
console.log(`[SDK Cache] SKIP ${methodPath} (no edgeCache, REALTIME)`);
|
|
199
|
-
}
|
|
200
|
-
pushRecent({ op: "SKIP", key: methodPath, ms: 0, ts: Date.now() });
|
|
201
|
-
return method(...args);
|
|
202
|
-
}
|
|
203
|
-
const argsHash = hashArgs(args);
|
|
204
|
-
const cacheKey = `${prefix}sdk:${methodPath}:${argsHash}`;
|
|
205
|
-
const start = Date.now();
|
|
206
|
-
const stats = getOrCreateStats(methodPath);
|
|
207
|
-
try {
|
|
208
|
-
const cached = await cacheGet(cacheKey);
|
|
209
|
-
if (cached !== null) {
|
|
210
|
-
const parsed = JSON.parse(cached);
|
|
211
|
-
const ms2 = Date.now() - start;
|
|
212
|
-
stats.hits++;
|
|
213
|
-
stats.totalMs += ms2;
|
|
214
|
-
if (debugEnabled2) {
|
|
215
|
-
console.log(`[SDK Cache] HIT ${methodPath}:${argsHash} (${ms2}ms)`);
|
|
216
|
-
}
|
|
217
|
-
pushRecent({
|
|
218
|
-
op: "HIT",
|
|
219
|
-
key: `${methodPath}:${argsHash}`,
|
|
220
|
-
ms: ms2,
|
|
221
|
-
ts: Date.now()
|
|
222
|
-
});
|
|
223
|
-
return parsed;
|
|
224
|
-
}
|
|
225
|
-
} catch {
|
|
226
|
-
stats.errors++;
|
|
227
|
-
}
|
|
228
|
-
const result = await method(...args);
|
|
229
|
-
const ms = Date.now() - start;
|
|
230
|
-
stats.misses++;
|
|
231
|
-
stats.totalMs += ms;
|
|
232
|
-
if (debugEnabled2) {
|
|
233
|
-
console.log(
|
|
234
|
-
`[SDK Cache] MISS ${methodPath}:${argsHash} (${ms}ms -> cached, TTL ${formatTtl(ttlSeconds)})`
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
pushRecent({
|
|
238
|
-
op: "MISS",
|
|
239
|
-
key: `${methodPath}:${argsHash}`,
|
|
240
|
-
ms,
|
|
241
|
-
ts: Date.now()
|
|
242
|
-
});
|
|
243
|
-
try {
|
|
244
|
-
const serialized = JSON.stringify(result);
|
|
245
|
-
cacheSet(cacheKey, serialized, ttlSeconds).catch(() => {
|
|
246
|
-
});
|
|
247
|
-
} catch {
|
|
248
|
-
}
|
|
249
|
-
return result;
|
|
250
|
-
}
|
|
251
|
-
function formatAgo(ms) {
|
|
252
|
-
if (ms < 1e3) return "<1s";
|
|
253
|
-
const s = Math.floor(ms / 1e3);
|
|
254
|
-
if (s < 60) return `${s}s ago`;
|
|
255
|
-
const m = Math.floor(s / 60);
|
|
256
|
-
return `${m}m ago`;
|
|
257
|
-
}
|
|
258
|
-
function getCacheStats() {
|
|
259
|
-
let totalHits = 0;
|
|
260
|
-
let totalMisses = 0;
|
|
261
|
-
let totalErrors = 0;
|
|
262
|
-
const topKeys = [];
|
|
263
|
-
statsMap.forEach((s, key) => {
|
|
264
|
-
totalHits += s.hits;
|
|
265
|
-
totalMisses += s.misses;
|
|
266
|
-
totalErrors += s.errors;
|
|
267
|
-
const total2 = s.hits + s.misses;
|
|
268
|
-
topKeys.push({
|
|
269
|
-
key,
|
|
270
|
-
hits: s.hits,
|
|
271
|
-
misses: s.misses,
|
|
272
|
-
avgMs: total2 > 0 ? Math.round(s.totalMs / total2) : 0
|
|
273
|
-
});
|
|
274
|
-
});
|
|
275
|
-
topKeys.sort((a, b) => b.hits - a.hits);
|
|
276
|
-
const total = totalHits + totalMisses;
|
|
277
|
-
const hitRate = total > 0 ? (totalHits / total * 100).toFixed(1) + "%" : "0%";
|
|
278
|
-
const now = Date.now();
|
|
279
|
-
const recent = recentOps.slice(-20).reverse().map((op) => ({
|
|
280
|
-
op: op.op,
|
|
281
|
-
key: op.key,
|
|
282
|
-
ms: op.ms,
|
|
283
|
-
ago: formatAgo(now - op.ts)
|
|
284
|
-
}));
|
|
285
|
-
return {
|
|
286
|
-
stats: { hits: totalHits, misses: totalMisses, errors: totalErrors, hitRate },
|
|
287
|
-
topKeys: topKeys.slice(0, 15),
|
|
288
|
-
recentOps: recent
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
135
|
// src/sdk-call.ts
|
|
293
136
|
async function sdkCall(method, ...args) {
|
|
294
137
|
return method(...args);
|
|
@@ -318,12 +161,10 @@ var getServerQueryClient = cache(
|
|
|
318
161
|
export {
|
|
319
162
|
cacheGet,
|
|
320
163
|
cacheSet,
|
|
321
|
-
cachedSdkCall,
|
|
322
164
|
createQueryOptions,
|
|
323
165
|
createServerQueryClient,
|
|
324
166
|
createSuspenseQueryOptions,
|
|
325
167
|
env,
|
|
326
|
-
getCacheStats,
|
|
327
168
|
getCircuitState,
|
|
328
169
|
getServerQueryClient,
|
|
329
170
|
isDev,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/environment.ts","../src/cache/redis-client.ts","../src/cache/sdk-cache.ts","../src/sdk-call.ts","../src/server-query-client.ts"],"sourcesContent":["/**\n * Environment detection that works across server, client, and containers.\n *\n * Priority:\n * 1. DEPLOYMENT_ENV (\"dev\" → staging, \"live\" → production)\n * 2. Server-side NODE_ENV fallback\n * 3. Client-side hostname detection\n */\nfunction getEnvironment(): \"development\" | \"staging\" | \"production\" {\n const deploymentEnv = process.env.DEPLOYMENT_ENV;\n if (deploymentEnv === \"dev\") return \"staging\";\n if (deploymentEnv === \"live\") return \"production\";\n\n if (typeof window === \"undefined\") {\n if (process.env.NODE_ENV === \"development\") return \"development\";\n return \"production\";\n }\n\n const host = window.location.hostname;\n if (host.includes(\"localhost\") || host.includes(\"127.0.0.1\"))\n return \"development\";\n if (host.includes(\"agr-hosting.dev\")) return \"staging\";\n return \"production\";\n}\n\nexport const env = getEnvironment();\nexport const isDev = env === \"development\";\nexport const isStaging = env === \"staging\";\nexport const isProduction = env === \"production\";\n","import type Redis from \"ioredis\";\nimport { isDev, isStaging } from \"../environment\";\n\nconst CIRCUIT_BREAKER_THRESHOLD = 5;\nconst CIRCUIT_BREAKER_RESET_MS = 60_000;\n\ninterface RedisGlobalState {\n client: Redis | null;\n consecutiveFailures: number;\n circuitOpenUntil: number;\n}\n\nconst g = globalThis as unknown as { __redisState?: RedisGlobalState };\nif (!g.__redisState) {\n g.__redisState = {\n client: null,\n consecutiveFailures: 0,\n circuitOpenUntil: 0,\n };\n}\nconst state = g.__redisState;\n\nconst debugEnabled = isDev || isStaging;\n\nfunction log(...args: unknown[]) {\n if (debugEnabled) {\n console.log(\"[Redis]\", ...args);\n }\n}\n\nfunction isCircuitOpen(): boolean {\n if (state.circuitOpenUntil === 0) return false;\n if (Date.now() >= state.circuitOpenUntil) {\n state.circuitOpenUntil = 0;\n state.consecutiveFailures = 0;\n log(\"Circuit breaker reset -- retrying Redis\");\n return false;\n }\n return true;\n}\n\nfunction recordFailure() {\n state.consecutiveFailures++;\n if (state.consecutiveFailures >= CIRCUIT_BREAKER_THRESHOLD) {\n state.circuitOpenUntil = Date.now() + CIRCUIT_BREAKER_RESET_MS;\n log(\n `Circuit breaker OPEN -- skipping Redis for ${CIRCUIT_BREAKER_RESET_MS / 1000}s`,\n );\n }\n}\n\nfunction recordSuccess() {\n state.consecutiveFailures = 0;\n}\n\nfunction getRedisUrl(): string | undefined {\n const containerRedis = process.env.REDIS_SERVERS;\n if (containerRedis) return `redis://${containerRedis}`;\n\n if (isDev) return process.env.REDIS_URL_DEV;\n if (isStaging) return process.env.REDIS_URL_STAGING;\n return process.env.REDIS_URL_PROD;\n}\n\nfunction getClient(): Redis | null {\n if (state.client) return state.client;\n\n const url = getRedisUrl();\n if (!url) {\n log(\"No REDIS_URL -- cache disabled\");\n return null;\n }\n\n try {\n // Dynamic import at module level isn't possible, so we require ioredis\n // at runtime. It's an optional peer dep -- if not installed, cache is\n // silently disabled.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const IORedis = require(\"ioredis\") as typeof import(\"ioredis\").default;\n\n state.client = new IORedis(url, {\n maxRetriesPerRequest: 1,\n connectTimeout: 3000,\n lazyConnect: true,\n enableOfflineQueue: false,\n });\n\n state.client.on(\"error\", (err: Error) => {\n log(\"Connection error:\", err.message);\n recordFailure();\n });\n\n state.client.on(\"connect\", () => log(\"Connected\"));\n\n state.client.connect().catch(() => {\n /* handled by error event */\n });\n\n return state.client;\n } catch {\n log(\"Failed to create client (ioredis not installed?)\");\n return null;\n }\n}\n\nexport async function cacheGet(key: string): Promise<string | null> {\n if (isCircuitOpen()) return null;\n\n const client = getClient();\n if (!client) return null;\n\n try {\n const value = await client.get(key);\n recordSuccess();\n return value;\n } catch {\n recordFailure();\n return null;\n }\n}\n\nexport async function cacheSet(\n key: string,\n value: string,\n ttlSeconds: number,\n): Promise<void> {\n if (isCircuitOpen()) return;\n\n const client = getClient();\n if (!client) return;\n\n try {\n await client.setex(key, ttlSeconds, value);\n recordSuccess();\n } catch {\n recordFailure();\n }\n}\n\nexport function getCircuitState(): \"closed\" | \"open\" {\n return isCircuitOpen() ? \"open\" : \"closed\";\n}\n\nexport function isRedisConnected(): boolean {\n return state.client?.status === \"ready\";\n}\n","import crypto from \"crypto\";\nimport { cacheGet, cacheSet } from \"./redis-client\";\nimport { isDev, isStaging } from \"../environment\";\n\nconst CACHE_ENABLED = process.env.REDIS_CACHE_ENABLED !== \"false\";\nconst debugEnabled = isDev || isStaging;\n\ninterface MethodStats {\n hits: number;\n misses: number;\n errors: number;\n totalMs: number;\n}\n\ninterface RecentOp {\n op: \"HIT\" | \"MISS\" | \"SKIP\" | \"ERROR\";\n key: string;\n ms: number;\n ts: number;\n}\n\ninterface CacheGlobalState {\n statsMap: Map<string, MethodStats>;\n recentOps: RecentOp[];\n}\n\nconst g = globalThis as unknown as { __sdkCacheState?: CacheGlobalState };\nif (!g.__sdkCacheState) {\n g.__sdkCacheState = { statsMap: new Map(), recentOps: [] };\n}\nconst { statsMap, recentOps } = g.__sdkCacheState;\n\nconst MAX_RECENT = 50;\n\nfunction pushRecent(op: RecentOp) {\n recentOps.push(op);\n if (recentOps.length > MAX_RECENT) recentOps.shift();\n}\n\nfunction getOrCreateStats(method: string): MethodStats {\n let s = statsMap.get(method);\n if (!s) {\n s = { hits: 0, misses: 0, errors: 0, totalMs: 0 };\n statsMap.set(method, s);\n }\n return s;\n}\n\nfunction hashArgs(args: unknown[]): string {\n const normalized = JSON.stringify(args, (_, v) => {\n if (v && typeof v === \"object\" && !Array.isArray(v)) {\n return Object.keys(v)\n .sort()\n .reduce(\n (acc, k) => {\n acc[k] = v[k];\n return acc;\n },\n {} as Record<string, unknown>,\n );\n }\n return v;\n });\n return crypto\n .createHash(\"sha256\")\n .update(normalized)\n .digest(\"hex\")\n .slice(0, 8);\n}\n\n/**\n * Extracts the `edgeCache` value from the arguments and converts it to\n * a TTL in seconds. Supports both the legacy numeric-hours format and the\n * augur-api >= 0.9.6 sub-hour string formats ('30s', '1m', '5m').\n *\n * - Number values (1, 2, 3, 4, 5, 8): treated as hours\n * - Numeric strings ('1', '2', etc.): treated as hours\n * - Duration strings ('30s', '1m', '5m'): parsed as seconds/minutes\n */\nfunction extractTtlSeconds(args: unknown[]): number | undefined {\n for (let i = args.length - 1; i >= 0; i--) {\n const arg = args[i];\n if (arg && typeof arg === \"object\" && \"edgeCache\" in arg) {\n const val = (arg as { edgeCache: unknown }).edgeCache;\n if (typeof val === \"number\") return val * 3600;\n if (typeof val === \"string\") {\n if (val.endsWith(\"s\")) return parseInt(val, 10);\n if (val.endsWith(\"m\")) return parseInt(val, 10) * 60;\n const n = Number(val);\n return isNaN(n) ? undefined : n * 3600;\n }\n return undefined;\n }\n }\n return undefined;\n}\n\nfunction formatTtl(seconds: number): string {\n if (seconds < 60) return `${seconds}s`;\n if (seconds < 3600) return `${Math.round(seconds / 60)}m`;\n return `${Math.round(seconds / 3600)}h`;\n}\n\n/**\n * Wraps an SDK call with Redis caching.\n *\n * @param prefix - Redis key prefix (e.g. \"mysite:\")\n * @param methodPath - Dot-separated SDK method path (used as cache key)\n * @param method - The SDK method to call\n * @param args - Arguments to pass to the SDK method. If any argument\n * contains an `edgeCache` property, the result is cached in Redis for\n * that duration. Without `edgeCache`, the call bypasses cache.\n *\n * Accepted `edgeCache` values (matching augur-api CacheParams):\n * - Numbers: 1–8 (hours)\n * - Sub-hour strings: '30s', '1m', '5m'\n *\n * @example\n * ```ts\n * const result = await cachedSdkCall(\n * \"mysite:\",\n * \"items.categories.get\",\n * augurServices.items.categories.get,\n * itemCategoryUid,\n * { edgeCache: 1 },\n * );\n * ```\n */\n// Strict overload (preferred when types match)\nexport async function cachedSdkCall<TArgs extends unknown[], TResult>(\n prefix: string,\n methodPath: string,\n method: (...args: TArgs) => Promise<TResult>,\n ...args: TArgs\n): Promise<TResult>;\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// Relaxed overload for SDK methods whose types don't include edgeCache params\nexport async function cachedSdkCall(\n prefix: string,\n methodPath: string,\n method: (...args: any[]) => Promise<any>,\n ...args: any[]\n): Promise<any>;\n/* eslint-enable @typescript-eslint/no-explicit-any */\n// Implementation\nexport async function cachedSdkCall<TArgs extends unknown[], TResult>(\n prefix: string,\n methodPath: string,\n method: (...args: TArgs) => Promise<TResult>,\n ...args: TArgs\n): Promise<TResult> {\n const ttlSeconds = extractTtlSeconds(args);\n\n if (!CACHE_ENABLED || ttlSeconds === undefined) {\n if (debugEnabled) {\n console.log(`[SDK Cache] SKIP ${methodPath} (no edgeCache, REALTIME)`);\n }\n pushRecent({ op: \"SKIP\", key: methodPath, ms: 0, ts: Date.now() });\n return method(...args);\n }\n\n const argsHash = hashArgs(args);\n const cacheKey = `${prefix}sdk:${methodPath}:${argsHash}`;\n const start = Date.now();\n const stats = getOrCreateStats(methodPath);\n\n try {\n const cached = await cacheGet(cacheKey);\n if (cached !== null) {\n const parsed = JSON.parse(cached);\n const ms = Date.now() - start;\n stats.hits++;\n stats.totalMs += ms;\n if (debugEnabled) {\n console.log(`[SDK Cache] HIT ${methodPath}:${argsHash} (${ms}ms)`);\n }\n pushRecent({\n op: \"HIT\",\n key: `${methodPath}:${argsHash}`,\n ms,\n ts: Date.now(),\n });\n return parsed as TResult;\n }\n } catch {\n stats.errors++;\n }\n\n const result = await method(...args);\n const ms = Date.now() - start;\n stats.misses++;\n stats.totalMs += ms;\n\n if (debugEnabled) {\n console.log(\n `[SDK Cache] MISS ${methodPath}:${argsHash} (${ms}ms -> cached, TTL ${formatTtl(ttlSeconds)})`,\n );\n }\n pushRecent({\n op: \"MISS\",\n key: `${methodPath}:${argsHash}`,\n ms,\n ts: Date.now(),\n });\n\n try {\n const serialized = JSON.stringify(result);\n cacheSet(cacheKey, serialized, ttlSeconds).catch(() => {\n /* swallow */\n });\n } catch {\n // Non-serializable result -- skip caching\n }\n\n return result;\n}\n\nfunction formatAgo(ms: number): string {\n if (ms < 1000) return \"<1s\";\n const s = Math.floor(ms / 1000);\n if (s < 60) return `${s}s ago`;\n const m = Math.floor(s / 60);\n return `${m}m ago`;\n}\n\n/** Returns aggregated cache stats for monitoring endpoints. */\nexport function getCacheStats() {\n let totalHits = 0;\n let totalMisses = 0;\n let totalErrors = 0;\n\n const topKeys: {\n key: string;\n hits: number;\n misses: number;\n avgMs: number;\n }[] = [];\n\n statsMap.forEach((s, key) => {\n totalHits += s.hits;\n totalMisses += s.misses;\n totalErrors += s.errors;\n const total = s.hits + s.misses;\n topKeys.push({\n key,\n hits: s.hits,\n misses: s.misses,\n avgMs: total > 0 ? Math.round(s.totalMs / total) : 0,\n });\n });\n\n topKeys.sort((a, b) => b.hits - a.hits);\n\n const total = totalHits + totalMisses;\n const hitRate =\n total > 0 ? ((totalHits / total) * 100).toFixed(1) + \"%\" : \"0%\";\n\n const now = Date.now();\n const recent = recentOps\n .slice(-20)\n .reverse()\n .map((op) => ({\n op: op.op,\n key: op.key,\n ms: op.ms,\n ago: formatAgo(now - op.ts),\n }));\n\n return {\n stats: { hits: totalHits, misses: totalMisses, errors: totalErrors, hitRate },\n topKeys: topKeys.slice(0, 15),\n recentOps: recent,\n };\n}\n","/**\n * Calls an Augur SDK method, forwarding all arguments with full type safety.\n *\n * With augur-api >= 0.9.6, SDK methods properly declare their `CacheParams`\n * option (including `edgeCache`), so this wrapper preserves both parameter\n * and return-type inference end-to-end.\n *\n * @example\n * ```ts\n * const result = await sdkCall(\n * augurServices.items.invMast.get,\n * invMastUid,\n * { edgeCache: 4 },\n * );\n * ```\n */\nexport async function sdkCall<TArgs extends unknown[], TResult>(\n method: (...args: TArgs) => Promise<TResult>,\n ...args: TArgs\n): Promise<TResult> {\n return method(...args);\n}\n","import { cache } from \"react\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport { CACHE_CONFIG } from \"@simpleapps-com/augur-utils\";\n\n/**\n * Creates a server-side query client optimised for prefetching.\n *\n * - No persistence (server-only)\n * - Longer cache times for prefetched data\n * - No retries (fail fast on server)\n * - No refetching (server renders are one-shot)\n */\nexport function createServerQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: CACHE_CONFIG.STATIC.staleTime,\n gcTime: CACHE_CONFIG.STATIC.staleTime,\n retry: 0,\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n },\n },\n });\n}\n\n/**\n * Returns a per-request singleton QueryClient for React Server Components.\n *\n * React's `cache()` deduplicates calls within a single server request,\n * so each request gets its own QueryClient while avoiding the cross-request\n * state leakage that a module-level singleton would cause.\n */\nexport const getServerQueryClient = cache(\n (): QueryClient => createServerQueryClient(),\n);\n"],"mappings":"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","names":["debugEnabled","g","ms","total"]}
|
|
1
|
+
{"version":3,"sources":["../src/environment.ts","../src/cache/redis-client.ts","../src/sdk-call.ts","../src/server-query-client.ts"],"sourcesContent":["/**\n * Environment detection that works across server, client, and containers.\n *\n * Priority:\n * 1. DEPLOYMENT_ENV (\"dev\" → staging, \"live\" → production)\n * 2. Server-side NODE_ENV fallback\n * 3. Client-side hostname detection\n */\nfunction getEnvironment(): \"development\" | \"staging\" | \"production\" {\n const deploymentEnv = process.env.DEPLOYMENT_ENV;\n if (deploymentEnv === \"dev\") return \"staging\";\n if (deploymentEnv === \"live\") return \"production\";\n\n if (typeof window === \"undefined\") {\n if (process.env.NODE_ENV === \"development\") return \"development\";\n return \"production\";\n }\n\n const host = window.location.hostname;\n if (host.includes(\"localhost\") || host.includes(\"127.0.0.1\"))\n return \"development\";\n if (host.includes(\"agr-hosting.dev\")) return \"staging\";\n return \"production\";\n}\n\nexport const env = getEnvironment();\nexport const isDev = env === \"development\";\nexport const isStaging = env === \"staging\";\nexport const isProduction = env === \"production\";\n","import type Redis from \"ioredis\";\nimport { isDev, isStaging } from \"../environment\";\n\nconst CIRCUIT_BREAKER_THRESHOLD = 5;\nconst CIRCUIT_BREAKER_RESET_MS = 60_000;\n\ninterface RedisGlobalState {\n client: Redis | null;\n consecutiveFailures: number;\n circuitOpenUntil: number;\n}\n\nconst g = globalThis as unknown as { __redisState?: RedisGlobalState };\nif (!g.__redisState) {\n g.__redisState = {\n client: null,\n consecutiveFailures: 0,\n circuitOpenUntil: 0,\n };\n}\nconst state = g.__redisState;\n\nconst debugEnabled = isDev || isStaging;\n\nfunction log(...args: unknown[]) {\n if (debugEnabled) {\n console.log(\"[Redis]\", ...args);\n }\n}\n\nfunction isCircuitOpen(): boolean {\n if (state.circuitOpenUntil === 0) return false;\n if (Date.now() >= state.circuitOpenUntil) {\n state.circuitOpenUntil = 0;\n state.consecutiveFailures = 0;\n log(\"Circuit breaker reset -- retrying Redis\");\n return false;\n }\n return true;\n}\n\nfunction recordFailure() {\n state.consecutiveFailures++;\n if (state.consecutiveFailures >= CIRCUIT_BREAKER_THRESHOLD) {\n state.circuitOpenUntil = Date.now() + CIRCUIT_BREAKER_RESET_MS;\n log(\n `Circuit breaker OPEN -- skipping Redis for ${CIRCUIT_BREAKER_RESET_MS / 1000}s`,\n );\n }\n}\n\nfunction recordSuccess() {\n state.consecutiveFailures = 0;\n}\n\nfunction getRedisUrl(): string | undefined {\n const containerRedis = process.env.REDIS_SERVERS;\n if (containerRedis) return `redis://${containerRedis}`;\n\n if (isDev) return process.env.REDIS_URL_DEV;\n if (isStaging) return process.env.REDIS_URL_STAGING;\n return process.env.REDIS_URL_PROD;\n}\n\nfunction getClient(): Redis | null {\n if (state.client) return state.client;\n\n const url = getRedisUrl();\n if (!url) {\n log(\"No REDIS_URL -- cache disabled\");\n return null;\n }\n\n try {\n // Dynamic import at module level isn't possible, so we require ioredis\n // at runtime. It's an optional peer dep -- if not installed, cache is\n // silently disabled.\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const IORedis = require(\"ioredis\") as typeof import(\"ioredis\").default;\n\n state.client = new IORedis(url, {\n maxRetriesPerRequest: 1,\n connectTimeout: 3000,\n lazyConnect: true,\n enableOfflineQueue: false,\n });\n\n state.client.on(\"error\", (err: Error) => {\n log(\"Connection error:\", err.message);\n recordFailure();\n });\n\n state.client.on(\"connect\", () => log(\"Connected\"));\n\n state.client.connect().catch(() => {\n /* handled by error event */\n });\n\n return state.client;\n } catch {\n log(\"Failed to create client (ioredis not installed?)\");\n return null;\n }\n}\n\nexport async function cacheGet(key: string): Promise<string | null> {\n if (isCircuitOpen()) return null;\n\n const client = getClient();\n if (!client) return null;\n\n try {\n const value = await client.get(key);\n recordSuccess();\n return value;\n } catch {\n recordFailure();\n return null;\n }\n}\n\nexport async function cacheSet(\n key: string,\n value: string,\n ttlSeconds: number,\n): Promise<void> {\n if (isCircuitOpen()) return;\n\n const client = getClient();\n if (!client) return;\n\n try {\n await client.setex(key, ttlSeconds, value);\n recordSuccess();\n } catch {\n recordFailure();\n }\n}\n\nexport function getCircuitState(): \"closed\" | \"open\" {\n return isCircuitOpen() ? \"open\" : \"closed\";\n}\n\nexport function isRedisConnected(): boolean {\n return state.client?.status === \"ready\";\n}\n","/**\n * Calls an Augur SDK method, forwarding all arguments with full type safety.\n *\n * With augur-api >= 0.9.6, SDK methods properly declare their `CacheParams`\n * option (including `edgeCache`), so this wrapper preserves both parameter\n * and return-type inference end-to-end.\n *\n * @example\n * ```ts\n * const result = await sdkCall(\n * augurServices.items.invMast.get,\n * invMastUid,\n * { edgeCache: 4 },\n * );\n * ```\n */\nexport async function sdkCall<TArgs extends unknown[], TResult>(\n method: (...args: TArgs) => Promise<TResult>,\n ...args: TArgs\n): Promise<TResult> {\n return method(...args);\n}\n","import { cache } from \"react\";\nimport { QueryClient } from \"@tanstack/react-query\";\nimport { CACHE_CONFIG } from \"@simpleapps-com/augur-utils\";\n\n/**\n * Creates a server-side query client optimised for prefetching.\n *\n * - No persistence (server-only)\n * - Longer cache times for prefetched data\n * - No retries (fail fast on server)\n * - No refetching (server renders are one-shot)\n */\nexport function createServerQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: CACHE_CONFIG.STATIC.staleTime,\n gcTime: CACHE_CONFIG.STATIC.staleTime,\n retry: 0,\n refetchOnMount: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n },\n },\n });\n}\n\n/**\n * Returns a per-request singleton QueryClient for React Server Components.\n *\n * React's `cache()` deduplicates calls within a single server request,\n * so each request gets its own QueryClient while avoiding the cross-request\n * state leakage that a module-level singleton would cause.\n */\nexport const getServerQueryClient = cache(\n (): QueryClient => createServerQueryClient(),\n);\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simpleapps-com/augur-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Server-side utilities for Augur ecommerce sites (Redis caching, SDK helpers, auth)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"valibot": "^1.0.0",
|
|
36
|
-
"@simpleapps-com/augur-utils": "0.
|
|
36
|
+
"@simpleapps-com/augur-utils": "0.2.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@simpleapps-com/augur-api": "^0.9.6",
|