@snapback/cli 1.1.12 → 1.1.15
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/README.md +79 -18
- package/dist/SkippedTestDetector-AXTMWWHC.js +5 -0
- package/dist/SkippedTestDetector-QLSQV7K7.js +5 -0
- package/dist/analysis-6WTBZJH3.js +6 -0
- package/dist/analysis-C472LUGW.js +2475 -0
- package/dist/auth-HFJRXXG2.js +1446 -0
- package/dist/auto-provision-organization-SF6XM7X4.js +161 -0
- package/dist/chunk-23G5VYA3.js +4259 -0
- package/dist/{chunk-QAKFE3NE.js → chunk-4YTE4JEW.js} +3 -4
- package/dist/chunk-5EOPYJ4Y.js +12 -0
- package/dist/{chunk-G7QXHNGB.js → chunk-5SQA44V7.js} +1125 -32
- package/dist/{chunk-BW7RALUZ.js → chunk-7ADPL4Q3.js} +11 -4
- package/dist/chunk-CBGOC6RV.js +293 -0
- package/dist/chunk-DNEADD2G.js +3499 -0
- package/dist/{chunk-NKBZIXCN.js → chunk-DPWFZNMY.js} +122 -15
- package/dist/chunk-GQ73B37K.js +314 -0
- package/dist/chunk-HR34NJP7.js +6133 -0
- package/dist/chunk-ICKSHS3A.js +2264 -0
- package/dist/{chunk-KPETDXQO.js → chunk-OI2HNNT6.js} +565 -50
- package/dist/chunk-PL4HF4M2.js +593 -0
- package/dist/chunk-WS36HDEU.js +3735 -0
- package/dist/chunk-XYU5FFE3.js +111 -0
- package/dist/chunk-ZBQDE6WJ.js +108 -0
- package/dist/client-WIO6W447.js +8 -0
- package/dist/dist-E7E2T3DQ.js +9 -0
- package/dist/dist-TEWNOZYS.js +5 -0
- package/dist/dist-YZBJAYEJ.js +12 -0
- package/dist/index.js +65215 -26627
- package/dist/local-service-adapter-3JHN6G4O.js +6 -0
- package/dist/pioneer-oauth-hook-V2JKEXM7.js +12 -0
- package/dist/{secure-credentials-6UMEU22H.js → secure-credentials-UEPG7GWW.js} +15 -8
- package/dist/snapback-dir-MG7DTRMF.js +6 -0
- package/package.json +8 -42
- package/scripts/postinstall.mjs +2 -3
- package/dist/SkippedTestDetector-B3JZUE5G.js +0 -5
- package/dist/SkippedTestDetector-B3JZUE5G.js.map +0 -1
- package/dist/analysis-Z53F5FT2.js +0 -6
- package/dist/analysis-Z53F5FT2.js.map +0 -1
- package/dist/chunk-6MR2TINI.js +0 -27
- package/dist/chunk-6MR2TINI.js.map +0 -1
- package/dist/chunk-BW7RALUZ.js.map +0 -1
- package/dist/chunk-G7QXHNGB.js.map +0 -1
- package/dist/chunk-ISVRGBWT.js +0 -16223
- package/dist/chunk-ISVRGBWT.js.map +0 -1
- package/dist/chunk-KPETDXQO.js.map +0 -1
- package/dist/chunk-NKBZIXCN.js.map +0 -1
- package/dist/chunk-QAKFE3NE.js.map +0 -1
- package/dist/chunk-YOVA65PS.js +0 -12745
- package/dist/chunk-YOVA65PS.js.map +0 -1
- package/dist/dist-7UKXVKH3.js +0 -5
- package/dist/dist-7UKXVKH3.js.map +0 -1
- package/dist/dist-VDK7WEF4.js +0 -5
- package/dist/dist-VDK7WEF4.js.map +0 -1
- package/dist/dist-WKLJSPJT.js +0 -8
- package/dist/dist-WKLJSPJT.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/secure-credentials-6UMEU22H.js.map +0 -1
- package/dist/snapback-dir-T3CRQRY6.js +0 -6
- package/dist/snapback-dir-T3CRQRY6.js.map +0 -1
|
@@ -1,11 +1,93 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { __name } from './chunk-
|
|
1
|
+
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
|
2
|
+
import { __name } from './chunk-7ADPL4Q3.js';
|
|
3
3
|
import { mkdir, writeFile, access, constants, readFile, appendFile, stat } from 'fs/promises';
|
|
4
4
|
import { homedir } from 'os';
|
|
5
5
|
import { join, dirname } from 'path';
|
|
6
|
+
import { z } from 'zod';
|
|
6
7
|
|
|
8
|
+
process.env.SNAPBACK_CLI='true';
|
|
7
9
|
var SNAPBACK_DIR = ".snapback";
|
|
8
10
|
var GLOBAL_SNAPBACK_DIR = ".snapback";
|
|
11
|
+
var WorkspaceConfigSchema = z.object({
|
|
12
|
+
workspaceId: z.string().optional(),
|
|
13
|
+
tier: z.enum([
|
|
14
|
+
"free",
|
|
15
|
+
"pro"
|
|
16
|
+
]).optional(),
|
|
17
|
+
protectionLevel: z.enum([
|
|
18
|
+
"standard",
|
|
19
|
+
"strict"
|
|
20
|
+
]).optional(),
|
|
21
|
+
syncEnabled: z.boolean().optional(),
|
|
22
|
+
createdAt: z.string(),
|
|
23
|
+
updatedAt: z.string()
|
|
24
|
+
});
|
|
25
|
+
var WorkspaceVitalsSchema = z.object({
|
|
26
|
+
framework: z.string().optional(),
|
|
27
|
+
frameworkConfidence: z.number().optional(),
|
|
28
|
+
packageManager: z.enum([
|
|
29
|
+
"npm",
|
|
30
|
+
"pnpm",
|
|
31
|
+
"yarn",
|
|
32
|
+
"bun"
|
|
33
|
+
]).optional(),
|
|
34
|
+
typescript: z.object({
|
|
35
|
+
enabled: z.boolean(),
|
|
36
|
+
strict: z.boolean().optional(),
|
|
37
|
+
version: z.string().optional()
|
|
38
|
+
}).optional(),
|
|
39
|
+
criticalFiles: z.array(z.string()).optional(),
|
|
40
|
+
detectedAt: z.string()
|
|
41
|
+
});
|
|
42
|
+
var ProtectedFileSchema = z.object({
|
|
43
|
+
pattern: z.string(),
|
|
44
|
+
addedAt: z.string(),
|
|
45
|
+
reason: z.string().optional()
|
|
46
|
+
});
|
|
47
|
+
var SessionStateSchema = z.object({
|
|
48
|
+
id: z.string(),
|
|
49
|
+
task: z.string().optional(),
|
|
50
|
+
startedAt: z.string(),
|
|
51
|
+
snapshotCount: z.number(),
|
|
52
|
+
filesModified: z.number().optional()
|
|
53
|
+
});
|
|
54
|
+
var LearningEntrySchema = z.object({
|
|
55
|
+
id: z.string(),
|
|
56
|
+
type: z.enum([
|
|
57
|
+
"pattern",
|
|
58
|
+
"pitfall",
|
|
59
|
+
"efficiency",
|
|
60
|
+
"discovery",
|
|
61
|
+
"workflow"
|
|
62
|
+
]),
|
|
63
|
+
trigger: z.string(),
|
|
64
|
+
action: z.string(),
|
|
65
|
+
source: z.string(),
|
|
66
|
+
createdAt: z.string()
|
|
67
|
+
});
|
|
68
|
+
var ViolationEntrySchema = z.object({
|
|
69
|
+
type: z.string(),
|
|
70
|
+
file: z.string(),
|
|
71
|
+
message: z.string(),
|
|
72
|
+
count: z.number().optional(),
|
|
73
|
+
date: z.string(),
|
|
74
|
+
prevention: z.string().optional()
|
|
75
|
+
});
|
|
76
|
+
var GlobalCredentialsSchema = z.object({
|
|
77
|
+
accessToken: z.string(),
|
|
78
|
+
refreshToken: z.string().optional(),
|
|
79
|
+
email: z.string(),
|
|
80
|
+
tier: z.enum([
|
|
81
|
+
"free",
|
|
82
|
+
"pro"
|
|
83
|
+
]),
|
|
84
|
+
expiresAt: z.string().optional()
|
|
85
|
+
});
|
|
86
|
+
var GlobalConfigSchema = z.object({
|
|
87
|
+
apiUrl: z.string().optional(),
|
|
88
|
+
defaultWorkspace: z.string().optional(),
|
|
89
|
+
analytics: z.boolean().optional()
|
|
90
|
+
});
|
|
9
91
|
function getGlobalDir() {
|
|
10
92
|
return join(homedir(), GLOBAL_SNAPBACK_DIR);
|
|
11
93
|
}
|
|
@@ -155,7 +237,12 @@ async function deleteGlobalJson(relativePath) {
|
|
|
155
237
|
}
|
|
156
238
|
__name(deleteGlobalJson, "deleteGlobalJson");
|
|
157
239
|
async function getWorkspaceConfig(workspaceRoot) {
|
|
158
|
-
|
|
240
|
+
const data = await readSnapbackJson("config.json", workspaceRoot);
|
|
241
|
+
if (!data) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const result = WorkspaceConfigSchema.safeParse(data);
|
|
245
|
+
return result.success ? result.data : null;
|
|
159
246
|
}
|
|
160
247
|
__name(getWorkspaceConfig, "getWorkspaceConfig");
|
|
161
248
|
async function saveWorkspaceConfig(config, workspaceRoot) {
|
|
@@ -163,7 +250,12 @@ async function saveWorkspaceConfig(config, workspaceRoot) {
|
|
|
163
250
|
}
|
|
164
251
|
__name(saveWorkspaceConfig, "saveWorkspaceConfig");
|
|
165
252
|
async function getWorkspaceVitals(workspaceRoot) {
|
|
166
|
-
|
|
253
|
+
const data = await readSnapbackJson("vitals.json", workspaceRoot);
|
|
254
|
+
if (!data) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const result = WorkspaceVitalsSchema.safeParse(data);
|
|
258
|
+
return result.success ? result.data : null;
|
|
167
259
|
}
|
|
168
260
|
__name(getWorkspaceVitals, "getWorkspaceVitals");
|
|
169
261
|
async function saveWorkspaceVitals(vitals, workspaceRoot) {
|
|
@@ -171,7 +263,12 @@ async function saveWorkspaceVitals(vitals, workspaceRoot) {
|
|
|
171
263
|
}
|
|
172
264
|
__name(saveWorkspaceVitals, "saveWorkspaceVitals");
|
|
173
265
|
async function getProtectedFiles(workspaceRoot) {
|
|
174
|
-
|
|
266
|
+
const data = await readSnapbackJson("protected.json", workspaceRoot);
|
|
267
|
+
if (!data) {
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
const result = z.array(ProtectedFileSchema).safeParse(data);
|
|
271
|
+
return result.success ? result.data : [];
|
|
175
272
|
}
|
|
176
273
|
__name(getProtectedFiles, "getProtectedFiles");
|
|
177
274
|
async function saveProtectedFiles(files, workspaceRoot) {
|
|
@@ -179,7 +276,12 @@ async function saveProtectedFiles(files, workspaceRoot) {
|
|
|
179
276
|
}
|
|
180
277
|
__name(saveProtectedFiles, "saveProtectedFiles");
|
|
181
278
|
async function getCurrentSession(workspaceRoot) {
|
|
182
|
-
|
|
279
|
+
const data = await readSnapbackJson("session/current.json", workspaceRoot);
|
|
280
|
+
if (!data) {
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
const result = SessionStateSchema.safeParse(data);
|
|
284
|
+
return result.success ? result.data : null;
|
|
183
285
|
}
|
|
184
286
|
__name(getCurrentSession, "getCurrentSession");
|
|
185
287
|
async function saveCurrentSession(session, workspaceRoot) {
|
|
@@ -200,7 +302,8 @@ async function recordLearning(learning, workspaceRoot) {
|
|
|
200
302
|
}
|
|
201
303
|
__name(recordLearning, "recordLearning");
|
|
202
304
|
async function getLearnings(workspaceRoot) {
|
|
203
|
-
|
|
305
|
+
const data = await loadSnapbackJsonl("learnings/user-learnings.jsonl", workspaceRoot);
|
|
306
|
+
return data.filter((item) => LearningEntrySchema.safeParse(item).success);
|
|
204
307
|
}
|
|
205
308
|
__name(getLearnings, "getLearnings");
|
|
206
309
|
async function recordViolation(violation, workspaceRoot) {
|
|
@@ -208,12 +311,13 @@ async function recordViolation(violation, workspaceRoot) {
|
|
|
208
311
|
}
|
|
209
312
|
__name(recordViolation, "recordViolation");
|
|
210
313
|
async function getViolations(workspaceRoot) {
|
|
211
|
-
|
|
314
|
+
const data = await loadSnapbackJsonl("patterns/violations.jsonl", workspaceRoot);
|
|
315
|
+
return data.filter((item) => ViolationEntrySchema.safeParse(item).success);
|
|
212
316
|
}
|
|
213
317
|
__name(getViolations, "getViolations");
|
|
214
318
|
async function getCredentials() {
|
|
215
319
|
try {
|
|
216
|
-
const { getCredentialsSecure } = await import('./secure-credentials-
|
|
320
|
+
const { getCredentialsSecure } = await import('./secure-credentials-UEPG7GWW.js');
|
|
217
321
|
return await getCredentialsSecure();
|
|
218
322
|
} catch {
|
|
219
323
|
return readGlobalJson("credentials.json");
|
|
@@ -222,7 +326,7 @@ async function getCredentials() {
|
|
|
222
326
|
__name(getCredentials, "getCredentials");
|
|
223
327
|
async function saveCredentials(credentials) {
|
|
224
328
|
try {
|
|
225
|
-
const { saveCredentialsSecure } = await import('./secure-credentials-
|
|
329
|
+
const { saveCredentialsSecure } = await import('./secure-credentials-UEPG7GWW.js');
|
|
226
330
|
return await saveCredentialsSecure(credentials);
|
|
227
331
|
} catch {
|
|
228
332
|
await createGlobalDirectory();
|
|
@@ -232,7 +336,7 @@ async function saveCredentials(credentials) {
|
|
|
232
336
|
__name(saveCredentials, "saveCredentials");
|
|
233
337
|
async function clearCredentials() {
|
|
234
338
|
try {
|
|
235
|
-
const { clearCredentialsSecure } = await import('./secure-credentials-
|
|
339
|
+
const { clearCredentialsSecure } = await import('./secure-credentials-UEPG7GWW.js');
|
|
236
340
|
return await clearCredentialsSecure();
|
|
237
341
|
} catch {
|
|
238
342
|
await deleteGlobalJson("credentials.json");
|
|
@@ -240,7 +344,12 @@ async function clearCredentials() {
|
|
|
240
344
|
}
|
|
241
345
|
__name(clearCredentials, "clearCredentials");
|
|
242
346
|
async function getGlobalConfig() {
|
|
243
|
-
|
|
347
|
+
const data = await readGlobalJson("config.json");
|
|
348
|
+
if (!data) {
|
|
349
|
+
return null;
|
|
350
|
+
}
|
|
351
|
+
const result = GlobalConfigSchema.safeParse(data);
|
|
352
|
+
return result.success ? result.data : null;
|
|
244
353
|
}
|
|
245
354
|
__name(getGlobalConfig, "getGlobalConfig");
|
|
246
355
|
async function saveGlobalConfig(config) {
|
|
@@ -295,6 +404,4 @@ async function getStats(path) {
|
|
|
295
404
|
}
|
|
296
405
|
__name(getStats, "getStats");
|
|
297
406
|
|
|
298
|
-
export { appendSnapbackJsonl, clearCredentials, createGlobalDirectory, createSnapbackDirectory, deleteGlobalJson, endCurrentSession, findWorkspaceRoot, getCredentials, getCurrentSession, getGlobalConfig, getGlobalDir, getGlobalPath, getLearnings, getProtectedFiles, getStats, getViolations, getWorkspaceConfig, getWorkspaceDir, getWorkspacePath, getWorkspaceVitals, isLoggedIn, isSnapbackInitialized, loadSnapbackJsonl, pathExists, readGlobalJson, readSnapbackJson, recordLearning, recordViolation, saveCredentials, saveCurrentSession, saveGlobalConfig, saveProtectedFiles, saveWorkspaceConfig, saveWorkspaceVitals, writeGlobalJson, writeSnapbackJson };
|
|
299
|
-
//# sourceMappingURL=chunk-NKBZIXCN.js.map
|
|
300
|
-
//# sourceMappingURL=chunk-NKBZIXCN.js.map
|
|
407
|
+
export { GlobalConfigSchema, GlobalCredentialsSchema, LearningEntrySchema, ProtectedFileSchema, SessionStateSchema, ViolationEntrySchema, WorkspaceConfigSchema, WorkspaceVitalsSchema, appendSnapbackJsonl, clearCredentials, createGlobalDirectory, createSnapbackDirectory, deleteGlobalJson, endCurrentSession, findWorkspaceRoot, getCredentials, getCurrentSession, getGlobalConfig, getGlobalDir, getGlobalPath, getLearnings, getProtectedFiles, getStats, getViolations, getWorkspaceConfig, getWorkspaceDir, getWorkspacePath, getWorkspaceVitals, isLoggedIn, isSnapbackInitialized, loadSnapbackJsonl, pathExists, readGlobalJson, readSnapbackJson, recordLearning, recordViolation, saveCredentials, saveCurrentSession, saveGlobalConfig, saveProtectedFiles, saveWorkspaceConfig, saveWorkspaceVitals, writeGlobalJson, writeSnapbackJson };
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env node --no-warnings=ExperimentalWarning
|
|
2
|
+
import { createLogger, LogLevel } from './chunk-WS36HDEU.js';
|
|
3
|
+
import { __name } from './chunk-7ADPL4Q3.js';
|
|
4
|
+
import { createClient } from 'redis';
|
|
5
|
+
|
|
6
|
+
process.env.SNAPBACK_CLI='true';
|
|
7
|
+
createLogger({
|
|
8
|
+
name: "redis-factory",
|
|
9
|
+
level: LogLevel.INFO
|
|
10
|
+
});
|
|
11
|
+
function isSocketTimeoutError(cause) {
|
|
12
|
+
if (!cause) return false;
|
|
13
|
+
return cause?.name === "SocketTimeoutError" || cause?.message?.includes("socket timeout");
|
|
14
|
+
}
|
|
15
|
+
__name(isSocketTimeoutError, "isSocketTimeoutError");
|
|
16
|
+
|
|
17
|
+
// ../../packages/platform/dist/cache/redis-metrics.js
|
|
18
|
+
var LATENCY_THRESHOLDS = {
|
|
19
|
+
/** Under 100ms is healthy */
|
|
20
|
+
healthy: 100,
|
|
21
|
+
/** Under 500ms is degraded */
|
|
22
|
+
degraded: 500
|
|
23
|
+
};
|
|
24
|
+
var RedisMetricsCollector = class {
|
|
25
|
+
static {
|
|
26
|
+
__name(this, "RedisMetricsCollector");
|
|
27
|
+
}
|
|
28
|
+
reconnectAttempts = 0;
|
|
29
|
+
lastSuccessAt = null;
|
|
30
|
+
lastErrorAt = null;
|
|
31
|
+
lastError = null;
|
|
32
|
+
keyPrefix;
|
|
33
|
+
constructor(keyPrefix = "") {
|
|
34
|
+
this.keyPrefix = keyPrefix;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Record a successful operation
|
|
38
|
+
*/
|
|
39
|
+
recordSuccess() {
|
|
40
|
+
this.lastSuccessAt = Date.now();
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Record an error
|
|
44
|
+
*/
|
|
45
|
+
recordError(error) {
|
|
46
|
+
this.lastErrorAt = Date.now();
|
|
47
|
+
this.lastError = error.message;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Record a reconnection attempt
|
|
51
|
+
*/
|
|
52
|
+
recordReconnect() {
|
|
53
|
+
this.reconnectAttempts++;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Reset reconnection counter (after successful connection)
|
|
57
|
+
*/
|
|
58
|
+
resetReconnectCount() {
|
|
59
|
+
this.reconnectAttempts = 0;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Measure Redis latency using PING command
|
|
63
|
+
*/
|
|
64
|
+
async measureLatency(client) {
|
|
65
|
+
if (!client || !client.isReady) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const start = Date.now();
|
|
70
|
+
await client.ping();
|
|
71
|
+
const latency = Date.now() - start;
|
|
72
|
+
this.recordSuccess();
|
|
73
|
+
return latency;
|
|
74
|
+
} catch (error) {
|
|
75
|
+
this.recordError(error);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Collect comprehensive metrics
|
|
81
|
+
*/
|
|
82
|
+
async collect(client) {
|
|
83
|
+
const isConnected = client?.isReady ?? false;
|
|
84
|
+
const isOpen = client?.isOpen ?? false;
|
|
85
|
+
const latency = await this.measureLatency(client);
|
|
86
|
+
let status;
|
|
87
|
+
let message;
|
|
88
|
+
if (!isConnected) {
|
|
89
|
+
status = "unhealthy";
|
|
90
|
+
message = "Redis client not connected";
|
|
91
|
+
} else if (latency === null) {
|
|
92
|
+
status = "unhealthy";
|
|
93
|
+
message = "Redis PING failed";
|
|
94
|
+
} else if (latency < LATENCY_THRESHOLDS.healthy) {
|
|
95
|
+
status = "healthy";
|
|
96
|
+
message = `Redis latency: ${latency}ms`;
|
|
97
|
+
} else if (latency < LATENCY_THRESHOLDS.degraded) {
|
|
98
|
+
status = "degraded";
|
|
99
|
+
message = `Redis latency elevated: ${latency}ms`;
|
|
100
|
+
} else {
|
|
101
|
+
status = "unhealthy";
|
|
102
|
+
message = `Redis latency too high: ${latency}ms`;
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
isConnected,
|
|
106
|
+
isOpen,
|
|
107
|
+
latency,
|
|
108
|
+
status,
|
|
109
|
+
message,
|
|
110
|
+
lastSuccessAt: this.lastSuccessAt,
|
|
111
|
+
lastErrorAt: this.lastErrorAt,
|
|
112
|
+
lastError: this.lastError,
|
|
113
|
+
reconnectAttempts: this.reconnectAttempts,
|
|
114
|
+
keyPrefix: this.keyPrefix
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get current reconnection attempt count
|
|
119
|
+
*/
|
|
120
|
+
getReconnectAttempts() {
|
|
121
|
+
return this.reconnectAttempts;
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var metricsCollectors = /* @__PURE__ */ new Map();
|
|
125
|
+
function getMetricsCollector(keyPrefix = "") {
|
|
126
|
+
let collector = metricsCollectors.get(keyPrefix);
|
|
127
|
+
if (!collector) {
|
|
128
|
+
collector = new RedisMetricsCollector(keyPrefix);
|
|
129
|
+
metricsCollectors.set(keyPrefix, collector);
|
|
130
|
+
}
|
|
131
|
+
return collector;
|
|
132
|
+
}
|
|
133
|
+
__name(getMetricsCollector, "getMetricsCollector");
|
|
134
|
+
|
|
135
|
+
// ../../packages/platform/dist/cache/redis-client.js
|
|
136
|
+
var logger2 = createLogger({
|
|
137
|
+
name: "redis-client",
|
|
138
|
+
level: LogLevel.INFO
|
|
139
|
+
});
|
|
140
|
+
var KEY_PREFIX = "cache:";
|
|
141
|
+
var redisClient = null;
|
|
142
|
+
var redisAvailable = false;
|
|
143
|
+
var initializationPromise = null;
|
|
144
|
+
var metricsCollector = getMetricsCollector(KEY_PREFIX);
|
|
145
|
+
async function initializeRedis() {
|
|
146
|
+
if (initializationPromise) {
|
|
147
|
+
return initializationPromise;
|
|
148
|
+
}
|
|
149
|
+
initializationPromise = (async () => {
|
|
150
|
+
const redisUrl = process.env.REDIS_URL;
|
|
151
|
+
if (!redisUrl) {
|
|
152
|
+
logger2.warn("REDIS_URL not configured - using in-memory fallback for caching");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
redisClient = createClient({
|
|
157
|
+
url: redisUrl,
|
|
158
|
+
socket: {
|
|
159
|
+
// Connection timeout - how long to wait for initial connection
|
|
160
|
+
connectTimeout: 1e4,
|
|
161
|
+
// TCP keepalive - prevents silent connection drops
|
|
162
|
+
keepAlive: 5e3,
|
|
163
|
+
// Reconnection strategy with exponential backoff + jitter
|
|
164
|
+
reconnectStrategy: /* @__PURE__ */ __name((retries, cause) => {
|
|
165
|
+
if (isSocketTimeoutError(cause)) {
|
|
166
|
+
logger2.warn("Redis socket timeout - not reconnecting", {
|
|
167
|
+
cause: cause?.message
|
|
168
|
+
});
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (retries > 20) {
|
|
172
|
+
logger2.error("Redis max retries exceeded", {
|
|
173
|
+
retries,
|
|
174
|
+
cause: cause?.message
|
|
175
|
+
});
|
|
176
|
+
return new Error("Redis connection failed");
|
|
177
|
+
}
|
|
178
|
+
const baseDelay = Math.min(2 ** retries * 100, 3e4);
|
|
179
|
+
const jitter = Math.floor(Math.random() * 200);
|
|
180
|
+
return baseDelay + jitter;
|
|
181
|
+
}, "reconnectStrategy")
|
|
182
|
+
},
|
|
183
|
+
// Application-level ping to keep connection alive
|
|
184
|
+
pingInterval: 6e4
|
|
185
|
+
});
|
|
186
|
+
redisClient.on("error", (err) => {
|
|
187
|
+
if (err.message.includes("ECONNRESET") || err.message.includes("ECONNREFUSED")) {
|
|
188
|
+
logger2.debug("Redis connection error (will reconnect)", {
|
|
189
|
+
error: err.message
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
logger2.warn("Redis client error", {
|
|
193
|
+
error: err.message
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
redisAvailable = false;
|
|
197
|
+
metricsCollector.recordError(err);
|
|
198
|
+
});
|
|
199
|
+
redisClient.on("connect", () => {
|
|
200
|
+
redisAvailable = true;
|
|
201
|
+
metricsCollector.resetReconnectCount();
|
|
202
|
+
if (process.env.NODE_ENV !== "production") {
|
|
203
|
+
logger2.info("Redis connected for platform caching");
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
redisClient.on("ready", () => {
|
|
207
|
+
redisAvailable = true;
|
|
208
|
+
logger2.debug("Redis client ready for platform caching");
|
|
209
|
+
});
|
|
210
|
+
redisClient.on("reconnecting", () => {
|
|
211
|
+
metricsCollector.recordReconnect();
|
|
212
|
+
logger2.debug("Redis reconnecting for platform caching");
|
|
213
|
+
});
|
|
214
|
+
await redisClient.connect();
|
|
215
|
+
redisAvailable = true;
|
|
216
|
+
if (process.env.NODE_ENV !== "production") {
|
|
217
|
+
logger2.info("\u2705 Redis client initialized for platform caching with production config");
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
logger2.error("Redis initialization failed", {
|
|
221
|
+
error: error instanceof Error ? error.message : String(error)
|
|
222
|
+
});
|
|
223
|
+
redisAvailable = false;
|
|
224
|
+
}
|
|
225
|
+
})();
|
|
226
|
+
return initializationPromise;
|
|
227
|
+
}
|
|
228
|
+
__name(initializeRedis, "initializeRedis");
|
|
229
|
+
async function getCache(key) {
|
|
230
|
+
await initializeRedis();
|
|
231
|
+
if (!redisAvailable || !redisClient) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const value = await redisClient.get(key);
|
|
236
|
+
if (!value) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return JSON.parse(value);
|
|
240
|
+
} catch (error) {
|
|
241
|
+
logger2.error("Redis GET failed", {
|
|
242
|
+
key,
|
|
243
|
+
error
|
|
244
|
+
});
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
__name(getCache, "getCache");
|
|
249
|
+
async function setCache(key, value, ttlSeconds) {
|
|
250
|
+
await initializeRedis();
|
|
251
|
+
if (!redisAvailable || !redisClient) {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
const serialized = JSON.stringify(value);
|
|
256
|
+
if (ttlSeconds) {
|
|
257
|
+
await redisClient.set(key, serialized, {
|
|
258
|
+
EX: ttlSeconds
|
|
259
|
+
});
|
|
260
|
+
} else {
|
|
261
|
+
await redisClient.set(key, serialized);
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
264
|
+
} catch (error) {
|
|
265
|
+
logger2.error("Redis SET failed", {
|
|
266
|
+
key,
|
|
267
|
+
error
|
|
268
|
+
});
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
__name(setCache, "setCache");
|
|
273
|
+
async function deleteCache(key) {
|
|
274
|
+
await initializeRedis();
|
|
275
|
+
if (!redisAvailable || !redisClient) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
try {
|
|
279
|
+
await redisClient.del(key);
|
|
280
|
+
return true;
|
|
281
|
+
} catch (error) {
|
|
282
|
+
logger2.error("Redis DEL failed", {
|
|
283
|
+
key,
|
|
284
|
+
error
|
|
285
|
+
});
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
__name(deleteCache, "deleteCache");
|
|
290
|
+
async function closeRedis() {
|
|
291
|
+
if (redisClient) {
|
|
292
|
+
try {
|
|
293
|
+
await redisClient.quit();
|
|
294
|
+
logger2.info("Redis connection closed");
|
|
295
|
+
} catch (error) {
|
|
296
|
+
logger2.error("Error closing Redis connection", {
|
|
297
|
+
error
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
redisClient = null;
|
|
301
|
+
redisAvailable = false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
__name(closeRedis, "closeRedis");
|
|
305
|
+
function isRedisAvailable() {
|
|
306
|
+
return redisAvailable;
|
|
307
|
+
}
|
|
308
|
+
__name(isRedisAvailable, "isRedisAvailable");
|
|
309
|
+
function getRedisClient() {
|
|
310
|
+
return redisClient;
|
|
311
|
+
}
|
|
312
|
+
__name(getRedisClient, "getRedisClient");
|
|
313
|
+
|
|
314
|
+
export { closeRedis, deleteCache, getCache, getRedisClient, initializeRedis, isRedisAvailable, setCache };
|