+24
-24
package.json
+24
-24
package.json
···
1
1
{
2
-
"name": "cachet",
3
-
"version": "0.3.2",
4
-
"scripts": {
5
-
"test": "echo \"Error: no test specified\" && exit 1",
6
-
"dev": "bun run --watch src/index.ts",
7
-
"start": "bun run src/index.ts",
8
-
"build": "bun build --compile --outfile dist/cachet --production ./src/index.ts"
9
-
},
10
-
"dependencies": {
11
-
"@sentry/bun": "^9.40.0",
12
-
"@tqman/nice-logger": "^1.0.7",
13
-
"@types/node-cron": "^3.0.11",
14
-
"bottleneck": "^2.19.5",
15
-
"elysia": "1.1.26",
16
-
"node-cron": "^3.0.3",
17
-
"sentry": "^0.1.2"
18
-
},
19
-
"devDependencies": {
20
-
"@types/bun": "latest"
21
-
},
22
-
"private": true,
23
-
"peerDependencies": {
24
-
"typescript": "^5"
25
-
}
2
+
"name": "cachet",
3
+
"version": "0.3.2",
4
+
"scripts": {
5
+
"test": "echo \"Error: no test specified\" && exit 1",
6
+
"dev": "bun run --watch src/index.ts",
7
+
"start": "bun run src/index.ts",
8
+
"build": "bun build --compile --outfile dist/cachet --production ./src/index.ts"
9
+
},
10
+
"dependencies": {
11
+
"@sentry/bun": "^9.40.0",
12
+
"@tqman/nice-logger": "^1.0.7",
13
+
"@types/node-cron": "^3.0.11",
14
+
"bottleneck": "^2.19.5",
15
+
"elysia": "1.1.26",
16
+
"node-cron": "^3.0.3",
17
+
"sentry": "^0.1.2"
18
+
},
19
+
"devDependencies": {
20
+
"@types/bun": "latest"
21
+
},
22
+
"private": true,
23
+
"peerDependencies": {
24
+
"typescript": "^5"
25
+
}
26
26
}
+53
-28
src/cache.ts
+53
-28
src/cache.ts
···
125
125
/**
126
126
* Discriminated union for all analytics cache data types
127
127
*/
128
-
type AnalyticsCacheData =
129
-
| { type: 'analytics'; data: FullAnalyticsData }
130
-
| { type: 'essential'; data: EssentialStatsData }
131
-
| { type: 'charts'; data: ChartData }
132
-
| { type: 'useragents'; data: UserAgentData };
128
+
type AnalyticsCacheData =
129
+
| { type: "analytics"; data: FullAnalyticsData }
130
+
| { type: "essential"; data: EssentialStatsData }
131
+
| { type: "charts"; data: ChartData }
132
+
| { type: "useragents"; data: UserAgentData };
133
133
134
134
/**
135
135
* Type-safe analytics cache entry
···
142
142
/**
143
143
* Type guard functions for cache data
144
144
*/
145
-
function isAnalyticsData(data: AnalyticsCacheData): data is { type: 'analytics'; data: FullAnalyticsData } {
146
-
return data.type === 'analytics';
145
+
function isAnalyticsData(
146
+
data: AnalyticsCacheData,
147
+
): data is { type: "analytics"; data: FullAnalyticsData } {
148
+
return data.type === "analytics";
147
149
}
148
150
149
-
function isEssentialStatsData(data: AnalyticsCacheData): data is { type: 'essential'; data: EssentialStatsData } {
150
-
return data.type === 'essential';
151
+
function isEssentialStatsData(
152
+
data: AnalyticsCacheData,
153
+
): data is { type: "essential"; data: EssentialStatsData } {
154
+
return data.type === "essential";
151
155
}
152
156
153
-
function isChartData(data: AnalyticsCacheData): data is { type: 'charts'; data: ChartData } {
154
-
return data.type === 'charts';
157
+
function isChartData(
158
+
data: AnalyticsCacheData,
159
+
): data is { type: "charts"; data: ChartData } {
160
+
return data.type === "charts";
155
161
}
156
162
157
-
function isUserAgentData(data: AnalyticsCacheData): data is { type: 'useragents'; data: UserAgentData } {
158
-
return data.type === 'useragents';
163
+
function isUserAgentData(
164
+
data: AnalyticsCacheData,
165
+
): data is { type: "useragents"; data: UserAgentData } {
166
+
return data.type === "useragents";
159
167
}
160
168
161
169
/**
···
178
186
getAnalyticsData(key: string): FullAnalyticsData | null {
179
187
const cached = this.cache.get(key);
180
188
const now = Date.now();
181
-
182
-
if (cached && now - cached.timestamp < this.cacheTTL && isAnalyticsData(cached.data)) {
189
+
190
+
if (
191
+
cached &&
192
+
now - cached.timestamp < this.cacheTTL &&
193
+
isAnalyticsData(cached.data)
194
+
) {
183
195
return cached.data.data;
184
196
}
185
197
return null;
···
191
203
getEssentialStatsData(key: string): EssentialStatsData | null {
192
204
const cached = this.cache.get(key);
193
205
const now = Date.now();
194
-
195
-
if (cached && now - cached.timestamp < this.cacheTTL && isEssentialStatsData(cached.data)) {
206
+
207
+
if (
208
+
cached &&
209
+
now - cached.timestamp < this.cacheTTL &&
210
+
isEssentialStatsData(cached.data)
211
+
) {
196
212
return cached.data.data;
197
213
}
198
214
return null;
···
204
220
getChartData(key: string): ChartData | null {
205
221
const cached = this.cache.get(key);
206
222
const now = Date.now();
207
-
208
-
if (cached && now - cached.timestamp < this.cacheTTL && isChartData(cached.data)) {
223
+
224
+
if (
225
+
cached &&
226
+
now - cached.timestamp < this.cacheTTL &&
227
+
isChartData(cached.data)
228
+
) {
209
229
return cached.data.data;
210
230
}
211
231
return null;
···
217
237
getUserAgentData(key: string): UserAgentData | null {
218
238
const cached = this.cache.get(key);
219
239
const now = Date.now();
220
-
221
-
if (cached && now - cached.timestamp < this.cacheTTL && isUserAgentData(cached.data)) {
240
+
241
+
if (
242
+
cached &&
243
+
now - cached.timestamp < this.cacheTTL &&
244
+
isUserAgentData(cached.data)
245
+
) {
222
246
return cached.data.data;
223
247
}
224
248
return null;
···
228
252
* Set analytics data in cache with type safety
229
253
*/
230
254
setAnalyticsData(key: string, data: FullAnalyticsData): void {
231
-
this.setCacheEntry(key, { type: 'analytics', data });
255
+
this.setCacheEntry(key, { type: "analytics", data });
232
256
}
233
257
234
258
/**
235
259
* Set essential stats data in cache with type safety
236
260
*/
237
261
setEssentialStatsData(key: string, data: EssentialStatsData): void {
238
-
this.setCacheEntry(key, { type: 'essential', data });
262
+
this.setCacheEntry(key, { type: "essential", data });
239
263
}
240
264
241
265
/**
242
266
* Set chart data in cache with type safety
243
267
*/
244
268
setChartData(key: string, data: ChartData): void {
245
-
this.setCacheEntry(key, { type: 'charts', data });
269
+
this.setCacheEntry(key, { type: "charts", data });
246
270
}
247
271
248
272
/**
249
273
* Set user agent data in cache with type safety
250
274
*/
251
275
setUserAgentData(key: string, data: UserAgentData): void {
252
-
this.setCacheEntry(key, { type: 'useragents', data });
276
+
this.setCacheEntry(key, { type: "useragents", data });
253
277
}
254
278
255
279
/**
···
657
681
// Check memory usage
658
682
const memUsage = process.memoryUsage();
659
683
const bytesToMiB = (bytes: number) => bytes / 1024 / 1024;
660
-
684
+
661
685
const heapUsedMiB = bytesToMiB(memUsage.heapUsed);
662
686
const heapTotalMiB = bytesToMiB(memUsage.heapTotal);
663
-
const heapPercent = heapTotalMiB > 0 ? (heapUsedMiB / heapTotalMiB) * 100 : 0;
687
+
const heapPercent =
688
+
heapTotalMiB > 0 ? (heapUsedMiB / heapTotalMiB) * 100 : 0;
664
689
const rssMiB = bytesToMiB(memUsage.rss);
665
690
const externalMiB = bytesToMiB(memUsage.external || 0);
666
691
const arrayBuffersMiB = bytesToMiB(memUsage.arrayBuffers || 0);
667
-
692
+
668
693
checks.memoryUsage = {
669
694
heapUsed: Math.round(heapUsedMiB),
670
695
heapTotal: Math.round(heapTotalMiB),
+6
-1
src/handlers/index.ts
+6
-1
src/handlers/index.ts
···
29
29
30
30
if (detailed) {
31
31
const health = await cache.detailedHealthCheck();
32
-
const statusCode = health.status === "unhealthy" ? 503 : health.status === "degraded" ? 200 : 200;
32
+
const statusCode =
33
+
health.status === "unhealthy"
34
+
? 503
35
+
: health.status === "degraded"
36
+
? 200
37
+
: 200;
33
38
await recordAnalytics(statusCode);
34
39
return Response.json(health, { status: statusCode });
35
40
}
+24
-6
src/routes/api-routes.ts
+24
-6
src/routes/api-routes.ts
···
82
82
details: {
83
83
type: "object",
84
84
properties: {
85
-
heapUsedMiB: { type: "number", description: "Precise heap used in MiB" },
86
-
heapTotalMiB: { type: "number", description: "Precise heap total in MiB" },
87
-
heapPercent: { type: "number", description: "Precise heap percentage" },
88
-
rssMiB: { type: "number", description: "Resident Set Size in MiB" },
89
-
externalMiB: { type: "number", description: "External memory in MiB" },
90
-
arrayBuffersMiB: { type: "number", description: "Array buffers in MiB" },
85
+
heapUsedMiB: {
86
+
type: "number",
87
+
description: "Precise heap used in MiB",
88
+
},
89
+
heapTotalMiB: {
90
+
type: "number",
91
+
description: "Precise heap total in MiB",
92
+
},
93
+
heapPercent: {
94
+
type: "number",
95
+
description: "Precise heap percentage",
96
+
},
97
+
rssMiB: {
98
+
type: "number",
99
+
description: "Resident Set Size in MiB",
100
+
},
101
+
externalMiB: {
102
+
type: "number",
103
+
description: "External memory in MiB",
104
+
},
105
+
arrayBuffersMiB: {
106
+
type: "number",
107
+
description: "Array buffers in MiB",
108
+
},
91
109
},
92
110
},
93
111
},