@packmind/cli 0.3.4 → 0.4.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/main.cjs +1415 -93
- package/package.json +4 -1
package/main.cjs
CHANGED
|
@@ -13,6 +13,9 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
13
13
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
14
14
|
var __getProtoOf = Object.getPrototypeOf;
|
|
15
15
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
16
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
17
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
18
|
+
};
|
|
16
19
|
var __copyProps = (to, from, except, desc) => {
|
|
17
20
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
21
|
for (let key of __getOwnPropNames(from))
|
|
@@ -30,6 +33,48 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
33
|
mod
|
|
31
34
|
));
|
|
32
35
|
|
|
36
|
+
// apps/cli/package.json
|
|
37
|
+
var require_package = __commonJS({
|
|
38
|
+
"apps/cli/package.json"(exports2, module2) {
|
|
39
|
+
module2.exports = {
|
|
40
|
+
name: "@packmind/cli",
|
|
41
|
+
version: "0.4.0",
|
|
42
|
+
description: "A command-line interface for Packmind linting and code quality checks",
|
|
43
|
+
private: false,
|
|
44
|
+
bin: {
|
|
45
|
+
"packmind-cli": "./main.cjs"
|
|
46
|
+
},
|
|
47
|
+
main: "./main.cjs",
|
|
48
|
+
files: [
|
|
49
|
+
"main.cjs",
|
|
50
|
+
"*.wasm",
|
|
51
|
+
"stubs",
|
|
52
|
+
"CHANGELOG.MD"
|
|
53
|
+
],
|
|
54
|
+
keywords: [
|
|
55
|
+
"packmind",
|
|
56
|
+
"linting",
|
|
57
|
+
"code-quality",
|
|
58
|
+
"cli",
|
|
59
|
+
"static-analysis"
|
|
60
|
+
],
|
|
61
|
+
author: "Packmind",
|
|
62
|
+
license: "UNLICENSED",
|
|
63
|
+
repository: {
|
|
64
|
+
type: "git",
|
|
65
|
+
url: "git+https://github.com/PackmindHub/packmind.git",
|
|
66
|
+
directory: "apps/cli"
|
|
67
|
+
},
|
|
68
|
+
publishConfig: {
|
|
69
|
+
access: "public"
|
|
70
|
+
},
|
|
71
|
+
scripts: {
|
|
72
|
+
"packmind-cli": "ts-node --project tsconfig.app.json src/main.ts"
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
33
78
|
// apps/cli/src/main.ts
|
|
34
79
|
var import_chalk2 = __toESM(require("chalk"));
|
|
35
80
|
var import_cmd_ts3 = require("cmd-ts");
|
|
@@ -183,6 +228,12 @@ var createRecipesDeploymentId = brandedIdFactory();
|
|
|
183
228
|
// packages/types/src/deployments/StandardsDeploymentId.ts
|
|
184
229
|
var createStandardsDeploymentId = brandedIdFactory();
|
|
185
230
|
|
|
231
|
+
// packages/types/src/deployments/PackagesDeploymentId.ts
|
|
232
|
+
var createPackagesDeploymentId = brandedIdFactory();
|
|
233
|
+
|
|
234
|
+
// packages/types/src/deployments/Package.ts
|
|
235
|
+
var createPackageId = brandedIdFactory();
|
|
236
|
+
|
|
186
237
|
// packages/types/src/git/GitRepoId.ts
|
|
187
238
|
var createGitRepoId = brandedIdFactory();
|
|
188
239
|
|
|
@@ -407,6 +458,48 @@ var createDetectionProgramId = brandedIdFactory();
|
|
|
407
458
|
// packages/types/src/linter/RuleDetectionAssessment.ts
|
|
408
459
|
var createRuleDetectionAssessmentId = brandedIdFactory();
|
|
409
460
|
|
|
461
|
+
// packages/types/src/sse/SSEEvent.ts
|
|
462
|
+
function createProgramStatusChangeEvent(ruleId, language) {
|
|
463
|
+
return {
|
|
464
|
+
type: "PROGRAM_STATUS_CHANGE",
|
|
465
|
+
data: { ruleId, language },
|
|
466
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
function createAssessmentStatusChangeEvent(ruleId, language) {
|
|
470
|
+
return {
|
|
471
|
+
type: "ASSESSMENT_STATUS_CHANGE",
|
|
472
|
+
data: {
|
|
473
|
+
ruleId,
|
|
474
|
+
language
|
|
475
|
+
},
|
|
476
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function createDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId) {
|
|
480
|
+
return {
|
|
481
|
+
type: "DETECTION_HEURISTICS_UPDATED",
|
|
482
|
+
data: {
|
|
483
|
+
ruleId,
|
|
484
|
+
language,
|
|
485
|
+
detectionHeuristicsId
|
|
486
|
+
},
|
|
487
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
function createUserContextChangeEvent(userId, organizationId, changeType, role) {
|
|
491
|
+
return {
|
|
492
|
+
type: "USER_CONTEXT_CHANGE",
|
|
493
|
+
data: {
|
|
494
|
+
userId,
|
|
495
|
+
organizationId,
|
|
496
|
+
changeType,
|
|
497
|
+
role
|
|
498
|
+
},
|
|
499
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
410
503
|
// apps/cli/src/application/useCases/ExecuteSingleFileAstUseCase.ts
|
|
411
504
|
var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
|
|
412
505
|
constructor(linterExecutionUseCase) {
|
|
@@ -447,8 +540,8 @@ var import_util = require("util");
|
|
|
447
540
|
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
448
541
|
var origin = "GitService";
|
|
449
542
|
var GitService = class {
|
|
450
|
-
constructor(
|
|
451
|
-
this.logger =
|
|
543
|
+
constructor(logger2 = new PackmindLogger(origin)) {
|
|
544
|
+
this.logger = logger2;
|
|
452
545
|
}
|
|
453
546
|
async getGitRepositoryRoot(path5) {
|
|
454
547
|
try {
|
|
@@ -488,7 +581,7 @@ ${error.message}`
|
|
|
488
581
|
throw new Error("Failed to get Git branches: Unknown error");
|
|
489
582
|
}
|
|
490
583
|
}
|
|
491
|
-
async getGitRemoteUrl(repoPath,
|
|
584
|
+
async getGitRemoteUrl(repoPath, origin10) {
|
|
492
585
|
try {
|
|
493
586
|
const { stdout } = await execAsync("git remote -v", {
|
|
494
587
|
cwd: repoPath
|
|
@@ -498,10 +591,10 @@ ${error.message}`
|
|
|
498
591
|
throw new Error("No Git remotes found in the repository");
|
|
499
592
|
}
|
|
500
593
|
let selectedRemote;
|
|
501
|
-
if (
|
|
502
|
-
const foundRemote = remotes.find((remote) => remote.name ===
|
|
594
|
+
if (origin10) {
|
|
595
|
+
const foundRemote = remotes.find((remote) => remote.name === origin10);
|
|
503
596
|
if (!foundRemote) {
|
|
504
|
-
throw new Error(`Remote '${
|
|
597
|
+
throw new Error(`Remote '${origin10}' not found in repository`);
|
|
505
598
|
}
|
|
506
599
|
selectedRemote = foundRemote.url;
|
|
507
600
|
} else if (remotes.length === 1) {
|
|
@@ -591,8 +684,8 @@ var GetGitRemoteUrlUseCase = class {
|
|
|
591
684
|
this.gitRemoteUrlService = gitRemoteUrlService;
|
|
592
685
|
}
|
|
593
686
|
async execute(command3) {
|
|
594
|
-
const { path: repoPath, origin:
|
|
595
|
-
return this.gitRemoteUrlService.getGitRemoteUrl(repoPath,
|
|
687
|
+
const { path: repoPath, origin: origin10 } = command3;
|
|
688
|
+
return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin10);
|
|
596
689
|
}
|
|
597
690
|
};
|
|
598
691
|
|
|
@@ -720,10 +813,10 @@ var path2 = __toESM(require("path"));
|
|
|
720
813
|
var fs2 = __toESM(require("fs/promises"));
|
|
721
814
|
var origin2 = "LintFilesInDirectoryUseCase";
|
|
722
815
|
var LintFilesInDirectoryUseCase = class {
|
|
723
|
-
constructor(services, repositories,
|
|
816
|
+
constructor(services, repositories, logger2 = new PackmindLogger(origin2)) {
|
|
724
817
|
this.services = services;
|
|
725
818
|
this.repositories = repositories;
|
|
726
|
-
this.logger =
|
|
819
|
+
this.logger = logger2;
|
|
727
820
|
}
|
|
728
821
|
fileMatchesScope(filePath, scopePatterns) {
|
|
729
822
|
if (!scopePatterns || scopePatterns.length === 0) {
|
|
@@ -1113,7 +1206,7 @@ function decodeApiKey(apiKey) {
|
|
|
1113
1206
|
var PackmindGateway = class {
|
|
1114
1207
|
constructor(apiKey) {
|
|
1115
1208
|
this.apiKey = apiKey;
|
|
1116
|
-
this.getPullData = async () => {
|
|
1209
|
+
this.getPullData = async (command3) => {
|
|
1117
1210
|
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1118
1211
|
if (!decodedApiKey.isValid) {
|
|
1119
1212
|
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
@@ -1124,7 +1217,13 @@ var PackmindGateway = class {
|
|
|
1124
1217
|
throw new Error("Invalid JWT: missing organizationId");
|
|
1125
1218
|
}
|
|
1126
1219
|
const organizationId = jwtPayload.organization.id;
|
|
1127
|
-
const
|
|
1220
|
+
const queryParams = new URLSearchParams();
|
|
1221
|
+
if (command3.packagesSlugs && command3.packagesSlugs.length > 0) {
|
|
1222
|
+
command3.packagesSlugs.forEach((slug) => {
|
|
1223
|
+
queryParams.append("packageSlug", slug);
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
const url = `${host}/api/v0/organizations/${organizationId}/pull?${queryParams.toString()}`;
|
|
1128
1227
|
try {
|
|
1129
1228
|
const response = await fetch(url, {
|
|
1130
1229
|
method: "GET",
|
|
@@ -1142,7 +1241,9 @@ var PackmindGateway = class {
|
|
|
1142
1241
|
}
|
|
1143
1242
|
} catch {
|
|
1144
1243
|
}
|
|
1145
|
-
|
|
1244
|
+
const error = new Error(errorMsg);
|
|
1245
|
+
error.statusCode = response.status;
|
|
1246
|
+
throw error;
|
|
1146
1247
|
}
|
|
1147
1248
|
const result = await response.json();
|
|
1148
1249
|
return result;
|
|
@@ -1159,6 +1260,54 @@ var PackmindGateway = class {
|
|
|
1159
1260
|
);
|
|
1160
1261
|
}
|
|
1161
1262
|
};
|
|
1263
|
+
this.listPackages = async () => {
|
|
1264
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1265
|
+
if (!decodedApiKey.isValid) {
|
|
1266
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1267
|
+
}
|
|
1268
|
+
const { host, jwt } = decodedApiKey.payload;
|
|
1269
|
+
const jwtPayload = decodeJwt(jwt);
|
|
1270
|
+
if (!jwtPayload?.organization?.id) {
|
|
1271
|
+
throw new Error("Invalid JWT: missing organizationId");
|
|
1272
|
+
}
|
|
1273
|
+
const organizationId = jwtPayload.organization.id;
|
|
1274
|
+
const url = `${host}/api/v0/organizations/${organizationId}/packages`;
|
|
1275
|
+
try {
|
|
1276
|
+
const response = await fetch(url, {
|
|
1277
|
+
method: "GET",
|
|
1278
|
+
headers: {
|
|
1279
|
+
"Content-Type": "application/json",
|
|
1280
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
if (!response.ok) {
|
|
1284
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
1285
|
+
try {
|
|
1286
|
+
const errorBody = await response.json();
|
|
1287
|
+
if (errorBody && errorBody.message) {
|
|
1288
|
+
errorMsg = `${errorBody.message}`;
|
|
1289
|
+
}
|
|
1290
|
+
} catch {
|
|
1291
|
+
}
|
|
1292
|
+
const error = new Error(errorMsg);
|
|
1293
|
+
error.statusCode = response.status;
|
|
1294
|
+
throw error;
|
|
1295
|
+
}
|
|
1296
|
+
const result = await response.json();
|
|
1297
|
+
return result.packages;
|
|
1298
|
+
} catch (error) {
|
|
1299
|
+
const err = error;
|
|
1300
|
+
const code = err?.code || err?.cause?.code;
|
|
1301
|
+
if (code === "ECONNREFUSED" || code === "ENOTFOUND" || err?.name === "FetchError" || typeof err?.message === "string" && (err.message.includes("Failed to fetch") || err.message.includes("network") || err.message.includes("NetworkError"))) {
|
|
1302
|
+
throw new Error(
|
|
1303
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
throw new Error(
|
|
1307
|
+
`Failed to list packages: Error: ${err?.message || JSON.stringify(error)}`
|
|
1308
|
+
);
|
|
1309
|
+
}
|
|
1310
|
+
};
|
|
1162
1311
|
this.listExecutionPrograms = async (params) => {
|
|
1163
1312
|
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1164
1313
|
if (!decodedApiKey.isValid) {
|
|
@@ -1337,6 +1486,56 @@ var PackmindGateway = class {
|
|
|
1337
1486
|
);
|
|
1338
1487
|
}
|
|
1339
1488
|
};
|
|
1489
|
+
this.getPackageSummary = async ({
|
|
1490
|
+
slug
|
|
1491
|
+
}) => {
|
|
1492
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1493
|
+
if (!decodedApiKey.isValid) {
|
|
1494
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1495
|
+
}
|
|
1496
|
+
const { host, jwt } = decodedApiKey.payload;
|
|
1497
|
+
const jwtPayload = decodeJwt(jwt);
|
|
1498
|
+
if (!jwtPayload?.organization?.id) {
|
|
1499
|
+
throw new Error("Invalid JWT: missing organizationId");
|
|
1500
|
+
}
|
|
1501
|
+
const organizationId = jwtPayload.organization.id;
|
|
1502
|
+
const url = `${host}/api/v0/organizations/${organizationId}/packages/${encodeURIComponent(slug)}`;
|
|
1503
|
+
try {
|
|
1504
|
+
const response = await fetch(url, {
|
|
1505
|
+
method: "GET",
|
|
1506
|
+
headers: {
|
|
1507
|
+
"Content-Type": "application/json",
|
|
1508
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1509
|
+
}
|
|
1510
|
+
});
|
|
1511
|
+
if (!response.ok) {
|
|
1512
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
1513
|
+
try {
|
|
1514
|
+
const errorBody = await response.json();
|
|
1515
|
+
if (errorBody && errorBody.message) {
|
|
1516
|
+
errorMsg = `${errorBody.message}`;
|
|
1517
|
+
}
|
|
1518
|
+
} catch {
|
|
1519
|
+
}
|
|
1520
|
+
const error = new Error(errorMsg);
|
|
1521
|
+
error.statusCode = response.status;
|
|
1522
|
+
throw error;
|
|
1523
|
+
}
|
|
1524
|
+
const result = await response.json();
|
|
1525
|
+
return result;
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
const err = error;
|
|
1528
|
+
const code = err?.code || err?.cause?.code;
|
|
1529
|
+
if (code === "ECONNREFUSED" || code === "ENOTFOUND" || err?.name === "FetchError" || typeof err?.message === "string" && (err.message.includes("Failed to fetch") || err.message.includes("network") || err.message.includes("NetworkError"))) {
|
|
1530
|
+
throw new Error(
|
|
1531
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
throw new Error(
|
|
1535
|
+
`Failed to get package '${slug}': Error: ${err?.message || JSON.stringify(error)}`
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1340
1539
|
}
|
|
1341
1540
|
};
|
|
1342
1541
|
|
|
@@ -3011,9 +3210,9 @@ var TreeSitter17 = __toESM(require("web-tree-sitter"));
|
|
|
3011
3210
|
// packages/linter-execution/src/application/useCases/ExecuteLinterProgramsUseCase.ts
|
|
3012
3211
|
var origin3 = "ExecuteLinterProgramsUseCase";
|
|
3013
3212
|
var ExecuteLinterProgramsUseCase = class {
|
|
3014
|
-
constructor(linterAstAdapter = new LinterAstAdapter(),
|
|
3213
|
+
constructor(linterAstAdapter = new LinterAstAdapter(), logger2 = new PackmindLogger(origin3)) {
|
|
3015
3214
|
this.linterAstAdapter = linterAstAdapter;
|
|
3016
|
-
this.logger =
|
|
3215
|
+
this.logger = logger2;
|
|
3017
3216
|
}
|
|
3018
3217
|
async execute(command3) {
|
|
3019
3218
|
const { filePath, fileContent, language, programs } = command3;
|
|
@@ -3206,90 +3405,1078 @@ var ExecuteLinterProgramsUseCase = class {
|
|
|
3206
3405
|
}
|
|
3207
3406
|
};
|
|
3208
3407
|
|
|
3209
|
-
//
|
|
3210
|
-
var
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3408
|
+
// packages/node-utils/src/ai/prompts/OpenAIService.ts
|
|
3409
|
+
var import_openai = __toESM(require("openai"));
|
|
3410
|
+
|
|
3411
|
+
// packages/node-utils/src/config/infra/Infisical/InfisicalConfig.ts
|
|
3412
|
+
var import_sdk = require("@infisical/sdk");
|
|
3413
|
+
var origin4 = "InfisicalConfig";
|
|
3414
|
+
var InfisicalConfig = class {
|
|
3415
|
+
constructor(clientId, clientSecret, env, projectId, logger2 = new PackmindLogger(origin4)) {
|
|
3416
|
+
this.clientId = clientId;
|
|
3417
|
+
this.clientSecret = clientSecret;
|
|
3418
|
+
this.env = env;
|
|
3419
|
+
this.projectId = projectId;
|
|
3420
|
+
this.logger = logger2;
|
|
3421
|
+
this.logger.info("Initializing InfisicalConfig", { env, projectId });
|
|
3422
|
+
try {
|
|
3423
|
+
this.client = new import_sdk.InfisicalSDK({
|
|
3424
|
+
siteUrl: "https://eu.infisical.com"
|
|
3425
|
+
// Optional, defaults to https://app.infisical.com
|
|
3426
|
+
});
|
|
3427
|
+
this.logger.info("InfisicalSDK client created successfully", {
|
|
3428
|
+
siteUrl: "https://eu.infisical.com"
|
|
3429
|
+
});
|
|
3430
|
+
} catch (error) {
|
|
3431
|
+
this.logger.error("Failed to create InfisicalSDK client", {
|
|
3432
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3433
|
+
});
|
|
3434
|
+
throw error;
|
|
3435
|
+
}
|
|
3215
3436
|
}
|
|
3216
|
-
async
|
|
3217
|
-
|
|
3218
|
-
const result = {
|
|
3219
|
-
filesCreated: 0,
|
|
3220
|
-
filesUpdated: 0,
|
|
3221
|
-
filesDeleted: 0,
|
|
3222
|
-
errors: []
|
|
3223
|
-
};
|
|
3437
|
+
async initClient() {
|
|
3438
|
+
this.logger.info("Initializing Infisical client authentication");
|
|
3224
3439
|
try {
|
|
3225
|
-
|
|
3226
|
-
|
|
3440
|
+
this.logger.debug("Authenticating with Infisical using universal auth");
|
|
3441
|
+
await this.client.auth().universalAuth.login({
|
|
3442
|
+
clientId: this.clientId,
|
|
3443
|
+
clientSecret: this.clientSecret
|
|
3444
|
+
});
|
|
3445
|
+
this.logger.info("Infisical client authenticated successfully");
|
|
3446
|
+
} catch (error) {
|
|
3447
|
+
this.logger.error("Failed to authenticate Infisical client", {
|
|
3448
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3449
|
+
});
|
|
3450
|
+
throw error;
|
|
3451
|
+
}
|
|
3452
|
+
}
|
|
3453
|
+
async getValue(secretName) {
|
|
3454
|
+
this.logger.info("Retrieving secret from Infisical", {
|
|
3455
|
+
secretName,
|
|
3456
|
+
env: this.env,
|
|
3457
|
+
projectId: this.projectId
|
|
3458
|
+
});
|
|
3459
|
+
try {
|
|
3460
|
+
this.logger.debug("Fetching secret from Infisical API", { secretName });
|
|
3461
|
+
const nameSecret = await this.client.secrets().getSecret({
|
|
3462
|
+
projectId: this.projectId,
|
|
3463
|
+
environment: this.env,
|
|
3464
|
+
secretName
|
|
3465
|
+
});
|
|
3466
|
+
if (nameSecret?.secretValue) {
|
|
3467
|
+
this.logger.info("Secret retrieved from Infisical successfully", {
|
|
3468
|
+
secretName
|
|
3469
|
+
});
|
|
3470
|
+
return nameSecret.secretValue;
|
|
3471
|
+
} else {
|
|
3472
|
+
this.logger.warn("Secret not found or has no value in Infisical", {
|
|
3473
|
+
secretName
|
|
3474
|
+
});
|
|
3475
|
+
return null;
|
|
3476
|
+
}
|
|
3477
|
+
} catch (error) {
|
|
3478
|
+
this.logger.warn("Failed to retrieve secret from Infisical", {
|
|
3479
|
+
secretName,
|
|
3480
|
+
env: this.env,
|
|
3481
|
+
projectId: this.projectId,
|
|
3482
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3483
|
+
});
|
|
3484
|
+
throw error;
|
|
3485
|
+
}
|
|
3486
|
+
}
|
|
3487
|
+
};
|
|
3488
|
+
|
|
3489
|
+
// packages/node-utils/src/config/config/Configuration.ts
|
|
3490
|
+
var origin5 = "Configuration";
|
|
3491
|
+
var Configuration = class _Configuration {
|
|
3492
|
+
constructor() {
|
|
3493
|
+
this.initialized = false;
|
|
3494
|
+
this.initializationPromise = null;
|
|
3495
|
+
}
|
|
3496
|
+
static {
|
|
3497
|
+
this.logger = new PackmindLogger(
|
|
3498
|
+
origin5,
|
|
3499
|
+
"info" /* INFO */
|
|
3500
|
+
);
|
|
3501
|
+
}
|
|
3502
|
+
static getInstance(logger2) {
|
|
3503
|
+
if (logger2) {
|
|
3504
|
+
_Configuration.logger = logger2;
|
|
3505
|
+
}
|
|
3506
|
+
_Configuration.logger.debug("Getting Configuration instance");
|
|
3507
|
+
if (!_Configuration.instance) {
|
|
3508
|
+
_Configuration.logger.info("Creating new Configuration instance");
|
|
3509
|
+
_Configuration.instance = new _Configuration();
|
|
3510
|
+
}
|
|
3511
|
+
return _Configuration.instance;
|
|
3512
|
+
}
|
|
3513
|
+
async initialize(env) {
|
|
3514
|
+
if (this.initialized) {
|
|
3515
|
+
_Configuration.logger.debug("Configuration already initialized, skipping");
|
|
3516
|
+
return;
|
|
3517
|
+
}
|
|
3518
|
+
if (this.initializationPromise) {
|
|
3519
|
+
_Configuration.logger.debug(
|
|
3520
|
+
"Configuration initialization already in progress, waiting for completion"
|
|
3521
|
+
);
|
|
3522
|
+
await this.initializationPromise;
|
|
3523
|
+
return;
|
|
3524
|
+
}
|
|
3525
|
+
this.initializationPromise = this.performInitialization(env);
|
|
3526
|
+
try {
|
|
3527
|
+
await this.initializationPromise;
|
|
3528
|
+
} finally {
|
|
3529
|
+
this.initializationPromise = null;
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
async performInitialization(env) {
|
|
3533
|
+
_Configuration.logger.info("Initializing Configuration");
|
|
3534
|
+
const configurationMode = env["CONFIGURATION"]?.toLowerCase();
|
|
3535
|
+
_Configuration.logger.debug("Configuration mode detected", {
|
|
3536
|
+
mode: configurationMode
|
|
3537
|
+
});
|
|
3538
|
+
if (configurationMode === "infisical") {
|
|
3539
|
+
_Configuration.logger.info("Initializing Infisical configuration");
|
|
3540
|
+
const clientId = env["INFISICAL_CLIENT_ID"];
|
|
3541
|
+
const clientSecret = env["INFISICAL_CLIENT_SECRET"];
|
|
3542
|
+
const infisicalEnv = env["INFISICAL_ENV"];
|
|
3543
|
+
const projectId = env["INFISICAL_PROJECT_ID"];
|
|
3544
|
+
if (!clientId || !clientSecret || !infisicalEnv || !projectId) {
|
|
3545
|
+
_Configuration.logger.error(
|
|
3546
|
+
"Infisical configuration is incomplete, falling back to environment variables only",
|
|
3547
|
+
{
|
|
3548
|
+
hasClientId: !!clientId,
|
|
3549
|
+
hasClientSecret: !!clientSecret,
|
|
3550
|
+
hasInfisicalEnv: !!infisicalEnv,
|
|
3551
|
+
hasProjectId: !!projectId
|
|
3552
|
+
}
|
|
3553
|
+
);
|
|
3554
|
+
this.infisicalConfig = void 0;
|
|
3555
|
+
} else {
|
|
3227
3556
|
try {
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3557
|
+
this.infisicalConfig = new InfisicalConfig(
|
|
3558
|
+
clientId,
|
|
3559
|
+
clientSecret,
|
|
3560
|
+
infisicalEnv,
|
|
3561
|
+
projectId
|
|
3562
|
+
);
|
|
3563
|
+
_Configuration.logger.debug("Initializing Infisical client");
|
|
3564
|
+
await this.infisicalConfig.initClient();
|
|
3565
|
+
_Configuration.logger.info(
|
|
3566
|
+
"Infisical configuration initialized successfully"
|
|
3233
3567
|
);
|
|
3234
3568
|
} catch (error) {
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3569
|
+
_Configuration.logger.error(
|
|
3570
|
+
"Failed to initialize Infisical, falling back to environment variables only",
|
|
3571
|
+
{
|
|
3572
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3573
|
+
}
|
|
3238
3574
|
);
|
|
3575
|
+
this.infisicalConfig = void 0;
|
|
3239
3576
|
}
|
|
3240
3577
|
}
|
|
3241
|
-
|
|
3578
|
+
} else {
|
|
3579
|
+
_Configuration.logger.info(
|
|
3580
|
+
"Using environment variables only (no Infisical)"
|
|
3581
|
+
);
|
|
3582
|
+
}
|
|
3583
|
+
this.initialized = true;
|
|
3584
|
+
_Configuration.logger.info("Configuration initialization completed");
|
|
3585
|
+
}
|
|
3586
|
+
static async getConfigWithDefault(key, defaultValue) {
|
|
3587
|
+
const value = await _Configuration.getConfig(key);
|
|
3588
|
+
return value ?? defaultValue;
|
|
3589
|
+
}
|
|
3590
|
+
static async getConfig(key, env = process.env, logger2) {
|
|
3591
|
+
if (logger2) {
|
|
3592
|
+
_Configuration.logger = logger2;
|
|
3593
|
+
}
|
|
3594
|
+
_Configuration.logger.info("Getting configuration value", { key });
|
|
3595
|
+
try {
|
|
3596
|
+
const instance = _Configuration.getInstance();
|
|
3597
|
+
await instance.initialize(env);
|
|
3598
|
+
const envValue = env[key];
|
|
3599
|
+
if (envValue) {
|
|
3600
|
+
_Configuration.logger.debug(
|
|
3601
|
+
"Configuration value found in environment variables",
|
|
3602
|
+
{ key }
|
|
3603
|
+
);
|
|
3604
|
+
return envValue;
|
|
3605
|
+
}
|
|
3606
|
+
if (instance.infisicalConfig) {
|
|
3607
|
+
_Configuration.logger.debug(
|
|
3608
|
+
"Checking Infisical for configuration value",
|
|
3609
|
+
{ key }
|
|
3610
|
+
);
|
|
3242
3611
|
try {
|
|
3243
|
-
await
|
|
3612
|
+
const infisicalValue = await instance.infisicalConfig.getValue(key);
|
|
3613
|
+
if (infisicalValue) {
|
|
3614
|
+
_Configuration.logger.debug(
|
|
3615
|
+
"Configuration value found in Infisical",
|
|
3616
|
+
{
|
|
3617
|
+
key
|
|
3618
|
+
}
|
|
3619
|
+
);
|
|
3620
|
+
return infisicalValue;
|
|
3621
|
+
}
|
|
3244
3622
|
} catch (error) {
|
|
3245
|
-
|
|
3246
|
-
|
|
3623
|
+
_Configuration.logger.error(
|
|
3624
|
+
"Failed to retrieve value from Infisical, value not available",
|
|
3625
|
+
{
|
|
3626
|
+
key,
|
|
3627
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3628
|
+
}
|
|
3629
|
+
);
|
|
3247
3630
|
}
|
|
3248
3631
|
}
|
|
3632
|
+
_Configuration.logger.warn("Configuration value not found", { key });
|
|
3633
|
+
return null;
|
|
3249
3634
|
} catch (error) {
|
|
3250
|
-
|
|
3251
|
-
|
|
3635
|
+
_Configuration.logger.warn("Failed to get configuration value", {
|
|
3636
|
+
key,
|
|
3637
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3638
|
+
});
|
|
3639
|
+
return null;
|
|
3252
3640
|
}
|
|
3253
|
-
return result;
|
|
3254
3641
|
}
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3642
|
+
};
|
|
3643
|
+
|
|
3644
|
+
// packages/node-utils/src/cache/Cache.ts
|
|
3645
|
+
var import_ioredis = __toESM(require("ioredis"));
|
|
3646
|
+
var origin6 = "Cache";
|
|
3647
|
+
var Cache = class _Cache {
|
|
3648
|
+
constructor() {
|
|
3649
|
+
this.initialized = false;
|
|
3650
|
+
this.connectionConfig = {
|
|
3651
|
+
host: "redis",
|
|
3652
|
+
port: 6379,
|
|
3653
|
+
maxRetriesPerRequest: 3
|
|
3654
|
+
};
|
|
3267
3655
|
}
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3656
|
+
static {
|
|
3657
|
+
this.logger = new PackmindLogger(
|
|
3658
|
+
origin6,
|
|
3659
|
+
"info" /* INFO */
|
|
3660
|
+
);
|
|
3661
|
+
}
|
|
3662
|
+
static {
|
|
3663
|
+
// Default cache expiration time in seconds (5 minutes)
|
|
3664
|
+
this.DEFAULT_EXPIRATION_SECONDS = 300;
|
|
3665
|
+
}
|
|
3666
|
+
/**
|
|
3667
|
+
* Get the singleton instance of Cache
|
|
3668
|
+
*/
|
|
3669
|
+
static getInstance() {
|
|
3670
|
+
_Cache.logger.debug("Getting Cache instance");
|
|
3671
|
+
if (!_Cache.instance) {
|
|
3672
|
+
_Cache.logger.info("Creating new Cache instance");
|
|
3673
|
+
_Cache.instance = new _Cache();
|
|
3274
3674
|
}
|
|
3675
|
+
return _Cache.instance;
|
|
3275
3676
|
}
|
|
3276
|
-
|
|
3677
|
+
/**
|
|
3678
|
+
* Initialize the Redis client with configuration
|
|
3679
|
+
* This should be called during application startup
|
|
3680
|
+
*/
|
|
3681
|
+
async initialize() {
|
|
3682
|
+
if (this.initialized) {
|
|
3683
|
+
return;
|
|
3684
|
+
}
|
|
3685
|
+
_Cache.logger.info("Initializing Redis cache client");
|
|
3277
3686
|
try {
|
|
3278
|
-
await
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3687
|
+
const redisUri = await Configuration.getConfig("REDIS_URI");
|
|
3688
|
+
if (!redisUri) {
|
|
3689
|
+
throw new Error("REDIS_URI configuration is required");
|
|
3690
|
+
}
|
|
3691
|
+
_Cache.logger.info("Using REDIS_URI configuration");
|
|
3692
|
+
this.client = new import_ioredis.default(redisUri, {
|
|
3693
|
+
maxRetriesPerRequest: 3
|
|
3694
|
+
});
|
|
3695
|
+
this.client.on("error", (error) => {
|
|
3696
|
+
_Cache.logger.error("Redis cache client error", {
|
|
3697
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3698
|
+
});
|
|
3699
|
+
});
|
|
3700
|
+
this.client.on("connect", () => {
|
|
3701
|
+
_Cache.logger.info("Redis cache client connected successfully");
|
|
3702
|
+
});
|
|
3703
|
+
this.initialized = true;
|
|
3704
|
+
_Cache.logger.info("Cache initialization completed");
|
|
3705
|
+
} catch (error) {
|
|
3706
|
+
_Cache.logger.error("Failed to initialize cache", {
|
|
3707
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3708
|
+
});
|
|
3709
|
+
throw new Error(
|
|
3710
|
+
`Cache initialization failed: ${error instanceof Error ? error.message : String(error)}`
|
|
3711
|
+
);
|
|
3282
3712
|
}
|
|
3283
3713
|
}
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3714
|
+
/**
|
|
3715
|
+
* Set a value in the cache with optional expiration
|
|
3716
|
+
* @param key - The cache key
|
|
3717
|
+
* @param value - The value to cache (will be JSON serialized)
|
|
3718
|
+
* @param expirationSeconds - Expiration time in seconds (default: 300s)
|
|
3719
|
+
*/
|
|
3720
|
+
async set(key, value, expirationSeconds = _Cache.DEFAULT_EXPIRATION_SECONDS) {
|
|
3721
|
+
if (!this.initialized || !this.client) {
|
|
3722
|
+
_Cache.logger.warn("Cache not initialized, skipping set operation", {
|
|
3723
|
+
key
|
|
3724
|
+
});
|
|
3725
|
+
return;
|
|
3726
|
+
}
|
|
3727
|
+
try {
|
|
3728
|
+
const serializedValue = JSON.stringify(value);
|
|
3729
|
+
await this.client.setex(key, expirationSeconds, serializedValue);
|
|
3730
|
+
} catch (error) {
|
|
3731
|
+
_Cache.logger.warn(
|
|
3732
|
+
"Failed to set cache value, continuing without caching",
|
|
3733
|
+
{
|
|
3734
|
+
key,
|
|
3735
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3736
|
+
}
|
|
3737
|
+
);
|
|
3738
|
+
}
|
|
3739
|
+
}
|
|
3740
|
+
/**
|
|
3741
|
+
* Get a value from the cache
|
|
3742
|
+
* @param key - The cache key
|
|
3743
|
+
* @returns The cached value or null if not found, expired, or error occurred
|
|
3744
|
+
*/
|
|
3745
|
+
async get(key) {
|
|
3746
|
+
if (!this.initialized || !this.client) {
|
|
3747
|
+
_Cache.logger.warn(
|
|
3748
|
+
"Cache not initialized, returning null for get operation",
|
|
3749
|
+
{ key }
|
|
3750
|
+
);
|
|
3751
|
+
return null;
|
|
3752
|
+
}
|
|
3753
|
+
try {
|
|
3754
|
+
const serializedValue = await this.client.get(key);
|
|
3755
|
+
if (serializedValue === null) {
|
|
3756
|
+
return null;
|
|
3757
|
+
}
|
|
3758
|
+
const value = JSON.parse(serializedValue);
|
|
3759
|
+
return value;
|
|
3760
|
+
} catch (error) {
|
|
3761
|
+
_Cache.logger.warn("Failed to get cache value, returning null", {
|
|
3762
|
+
key,
|
|
3763
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3764
|
+
});
|
|
3765
|
+
return null;
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
/**
|
|
3769
|
+
* Invalidate (delete) a cache entry
|
|
3770
|
+
* @param key - The cache key to invalidate
|
|
3771
|
+
*/
|
|
3772
|
+
async invalidate(key) {
|
|
3773
|
+
if (!this.initialized || !this.client) {
|
|
3774
|
+
_Cache.logger.warn(
|
|
3775
|
+
"Cache not initialized, skipping invalidate operation",
|
|
3776
|
+
{ key }
|
|
3777
|
+
);
|
|
3778
|
+
return;
|
|
3779
|
+
}
|
|
3780
|
+
try {
|
|
3781
|
+
await this.client.del(key);
|
|
3782
|
+
} catch (error) {
|
|
3783
|
+
_Cache.logger.warn(
|
|
3784
|
+
"Failed to invalidate cache key, continuing without invalidation",
|
|
3785
|
+
{
|
|
3786
|
+
key,
|
|
3787
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3788
|
+
}
|
|
3789
|
+
);
|
|
3790
|
+
}
|
|
3791
|
+
}
|
|
3792
|
+
/**
|
|
3793
|
+
* Disconnect the Redis client (for cleanup during shutdown)
|
|
3794
|
+
*/
|
|
3795
|
+
async disconnect() {
|
|
3796
|
+
_Cache.logger.info("Disconnecting cache client");
|
|
3797
|
+
if (this.client) {
|
|
3798
|
+
try {
|
|
3799
|
+
await this.client.disconnect();
|
|
3800
|
+
_Cache.logger.info("Cache client disconnected successfully");
|
|
3801
|
+
} catch (error) {
|
|
3802
|
+
_Cache.logger.error("Error disconnecting cache client", {
|
|
3803
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3804
|
+
});
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
this.initialized = false;
|
|
3808
|
+
}
|
|
3809
|
+
/**
|
|
3810
|
+
* Get cache statistics (for monitoring/debugging)
|
|
3811
|
+
*/
|
|
3812
|
+
async getStats() {
|
|
3813
|
+
return {
|
|
3814
|
+
connected: this.client?.status === "ready",
|
|
3815
|
+
initialized: this.initialized
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
};
|
|
3819
|
+
|
|
3820
|
+
// packages/node-utils/src/jobs/infra/bullMQ/WorkerQueue.ts
|
|
3821
|
+
var import_bullmq2 = require("bullmq");
|
|
3822
|
+
|
|
3823
|
+
// packages/node-utils/src/jobs/infra/bullMQ/AbstractQueue.ts
|
|
3824
|
+
var import_bullmq = require("bullmq");
|
|
3825
|
+
|
|
3826
|
+
// packages/node-utils/src/jobs/infra/DelayedJobsFactory.ts
|
|
3827
|
+
var logger = new PackmindLogger("DelayedJobsFactory");
|
|
3828
|
+
|
|
3829
|
+
// packages/node-utils/src/mail/SmtpMailService.ts
|
|
3830
|
+
var import_nodemailer = __toESM(require("nodemailer"));
|
|
3831
|
+
|
|
3832
|
+
// packages/node-utils/src/nest/Public.ts
|
|
3833
|
+
var import_common = require("@nestjs/common");
|
|
3834
|
+
|
|
3835
|
+
// packages/node-utils/src/repositories/AbstractRepository.ts
|
|
3836
|
+
var import_common2 = require("@nestjs/common");
|
|
3837
|
+
|
|
3838
|
+
// packages/node-utils/src/sse/RedisSSEClient.ts
|
|
3839
|
+
var import_ioredis2 = __toESM(require("ioredis"));
|
|
3840
|
+
var origin7 = "RedisSSEClient";
|
|
3841
|
+
var RedisSSEClient = class _RedisSSEClient {
|
|
3842
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
3843
|
+
constructor() {
|
|
3844
|
+
this.initialized = false;
|
|
3845
|
+
}
|
|
3846
|
+
static {
|
|
3847
|
+
this.logger = new PackmindLogger(origin7);
|
|
3848
|
+
}
|
|
3849
|
+
static getInstance() {
|
|
3850
|
+
_RedisSSEClient.logger.debug("Getting RedisSSEClient instance");
|
|
3851
|
+
if (!_RedisSSEClient.instance) {
|
|
3852
|
+
_RedisSSEClient.logger.info("Creating new RedisSSEClient instance");
|
|
3853
|
+
_RedisSSEClient.instance = new _RedisSSEClient();
|
|
3854
|
+
}
|
|
3855
|
+
return _RedisSSEClient.instance;
|
|
3856
|
+
}
|
|
3857
|
+
/**
|
|
3858
|
+
* Initialize Redis clients using the same configuration as BullMQ
|
|
3859
|
+
*/
|
|
3860
|
+
async initialize() {
|
|
3861
|
+
if (this.initialized) return;
|
|
3862
|
+
_RedisSSEClient.logger.info("Initializing Redis SSE clients");
|
|
3863
|
+
try {
|
|
3864
|
+
const redisURI = await Configuration.getConfig("REDIS_URI") || "redis";
|
|
3865
|
+
this.publisherClient = new import_ioredis2.default(redisURI);
|
|
3866
|
+
this.subscriberClient = new import_ioredis2.default(redisURI);
|
|
3867
|
+
this.publisherClient.on("error", (error) => {
|
|
3868
|
+
_RedisSSEClient.logger.error("Redis publisher client error", {
|
|
3869
|
+
error: error.message
|
|
3870
|
+
});
|
|
3871
|
+
});
|
|
3872
|
+
this.subscriberClient.on("error", (error) => {
|
|
3873
|
+
_RedisSSEClient.logger.error("Redis subscriber client error", {
|
|
3874
|
+
error: error.message
|
|
3875
|
+
});
|
|
3876
|
+
});
|
|
3877
|
+
await this.publisherClient.ping();
|
|
3878
|
+
await this.subscriberClient.ping();
|
|
3879
|
+
this.initialized = true;
|
|
3880
|
+
_RedisSSEClient.logger.info("Redis SSE clients initialized successfully");
|
|
3881
|
+
} catch (error) {
|
|
3882
|
+
_RedisSSEClient.logger.error("Failed to initialize Redis SSE clients", {
|
|
3883
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3884
|
+
});
|
|
3885
|
+
throw error;
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
/**
|
|
3889
|
+
* Publish a message to a Redis channel
|
|
3890
|
+
*/
|
|
3891
|
+
async publish(channel, message) {
|
|
3892
|
+
await this.initialize();
|
|
3893
|
+
if (!this.publisherClient) {
|
|
3894
|
+
throw new Error("Publisher client not initialized");
|
|
3895
|
+
}
|
|
3896
|
+
_RedisSSEClient.logger.debug("Publishing message to Redis channel", {
|
|
3897
|
+
channel,
|
|
3898
|
+
messageLength: message.length
|
|
3899
|
+
});
|
|
3900
|
+
try {
|
|
3901
|
+
const subscriberCount = await this.publisherClient.publish(
|
|
3902
|
+
channel,
|
|
3903
|
+
message
|
|
3904
|
+
);
|
|
3905
|
+
_RedisSSEClient.logger.debug("Message published successfully", {
|
|
3906
|
+
channel,
|
|
3907
|
+
subscriberCount
|
|
3908
|
+
});
|
|
3909
|
+
return subscriberCount;
|
|
3910
|
+
} catch (error) {
|
|
3911
|
+
_RedisSSEClient.logger.error("Failed to publish message", {
|
|
3912
|
+
channel,
|
|
3913
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3914
|
+
});
|
|
3915
|
+
throw error;
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
/**
|
|
3919
|
+
* Subscribe to a Redis channel
|
|
3920
|
+
*/
|
|
3921
|
+
async subscribe(channel, callback) {
|
|
3922
|
+
await this.initialize();
|
|
3923
|
+
if (!this.subscriberClient) {
|
|
3924
|
+
throw new Error("Subscriber client not initialized");
|
|
3925
|
+
}
|
|
3926
|
+
_RedisSSEClient.logger.info("Subscribing to Redis channel", { channel });
|
|
3927
|
+
try {
|
|
3928
|
+
this.subscriberClient.on("message", (receivedChannel, message) => {
|
|
3929
|
+
if (receivedChannel === channel) {
|
|
3930
|
+
_RedisSSEClient.logger.debug("Received message from Redis channel", {
|
|
3931
|
+
channel: receivedChannel,
|
|
3932
|
+
messageLength: message.length
|
|
3933
|
+
});
|
|
3934
|
+
callback(message);
|
|
3935
|
+
}
|
|
3936
|
+
});
|
|
3937
|
+
await this.subscriberClient.subscribe(channel);
|
|
3938
|
+
_RedisSSEClient.logger.info("Successfully subscribed to Redis channel", {
|
|
3939
|
+
channel
|
|
3940
|
+
});
|
|
3941
|
+
} catch (error) {
|
|
3942
|
+
_RedisSSEClient.logger.error("Failed to subscribe to Redis channel", {
|
|
3943
|
+
channel,
|
|
3944
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3945
|
+
});
|
|
3946
|
+
throw error;
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
/**
|
|
3950
|
+
* Unsubscribe from a Redis channel
|
|
3951
|
+
*/
|
|
3952
|
+
async unsubscribe(channel) {
|
|
3953
|
+
if (!this.subscriberClient) {
|
|
3954
|
+
return;
|
|
3955
|
+
}
|
|
3956
|
+
_RedisSSEClient.logger.info("Unsubscribing from Redis channel", { channel });
|
|
3957
|
+
try {
|
|
3958
|
+
await this.subscriberClient.unsubscribe(channel);
|
|
3959
|
+
_RedisSSEClient.logger.info(
|
|
3960
|
+
"Successfully unsubscribed from Redis channel",
|
|
3961
|
+
{ channel }
|
|
3962
|
+
);
|
|
3963
|
+
} catch (error) {
|
|
3964
|
+
_RedisSSEClient.logger.error("Failed to unsubscribe from Redis channel", {
|
|
3965
|
+
channel,
|
|
3966
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3967
|
+
});
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
/**
|
|
3971
|
+
* Clean up Redis connections
|
|
3972
|
+
*/
|
|
3973
|
+
async disconnect() {
|
|
3974
|
+
_RedisSSEClient.logger.info("Disconnecting Redis SSE clients");
|
|
3975
|
+
try {
|
|
3976
|
+
if (this.publisherClient) {
|
|
3977
|
+
this.publisherClient.disconnect();
|
|
3978
|
+
}
|
|
3979
|
+
if (this.subscriberClient) {
|
|
3980
|
+
this.subscriberClient.disconnect();
|
|
3981
|
+
}
|
|
3982
|
+
this.initialized = false;
|
|
3983
|
+
_RedisSSEClient.logger.info("Redis SSE clients disconnected successfully");
|
|
3984
|
+
} catch (error) {
|
|
3985
|
+
_RedisSSEClient.logger.error("Error disconnecting Redis SSE clients", {
|
|
3986
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3987
|
+
});
|
|
3988
|
+
}
|
|
3989
|
+
}
|
|
3990
|
+
/**
|
|
3991
|
+
* Get connection status
|
|
3992
|
+
*/
|
|
3993
|
+
isConnected() {
|
|
3994
|
+
return this.initialized && this.publisherClient?.status === "ready" && this.subscriberClient?.status === "ready";
|
|
3995
|
+
}
|
|
3996
|
+
};
|
|
3997
|
+
|
|
3998
|
+
// packages/node-utils/src/sse/types.ts
|
|
3999
|
+
var SSE_REDIS_CHANNELS = {
|
|
4000
|
+
/**
|
|
4001
|
+
* Channel for subscription management messages
|
|
4002
|
+
* Used when clients subscribe/unsubscribe to specific event types
|
|
4003
|
+
*/
|
|
4004
|
+
SUBSCRIPTIONS: "sse:subscriptions",
|
|
4005
|
+
/**
|
|
4006
|
+
* Channel for event notifications
|
|
4007
|
+
* Used to broadcast SSE events to all API instances
|
|
4008
|
+
*/
|
|
4009
|
+
EVENTS: "sse:events"
|
|
4010
|
+
};
|
|
4011
|
+
function createSSEEventMessage(eventType, params, data, targetUserIds) {
|
|
4012
|
+
return {
|
|
4013
|
+
eventType,
|
|
4014
|
+
params,
|
|
4015
|
+
data,
|
|
4016
|
+
targetUserIds,
|
|
4017
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4018
|
+
};
|
|
4019
|
+
}
|
|
4020
|
+
function serializeSSERedisMessage(message) {
|
|
4021
|
+
try {
|
|
4022
|
+
return JSON.stringify(message);
|
|
4023
|
+
} catch (error) {
|
|
4024
|
+
throw new Error(
|
|
4025
|
+
`Failed to serialize SSE Redis message: ${error instanceof Error ? error.message : String(error)}`
|
|
4026
|
+
);
|
|
4027
|
+
}
|
|
4028
|
+
}
|
|
4029
|
+
|
|
4030
|
+
// packages/node-utils/src/sse/SSEEventPublisher.ts
|
|
4031
|
+
var origin8 = "SSEEventPublisher";
|
|
4032
|
+
var SSEEventPublisher = class _SSEEventPublisher {
|
|
4033
|
+
static {
|
|
4034
|
+
this.logger = new PackmindLogger(origin8);
|
|
4035
|
+
}
|
|
4036
|
+
/**
|
|
4037
|
+
* Get the singleton instance
|
|
4038
|
+
*/
|
|
4039
|
+
static getInstance() {
|
|
4040
|
+
_SSEEventPublisher.logger.debug("Getting SSEEventPublisher instance");
|
|
4041
|
+
if (!_SSEEventPublisher.instance) {
|
|
4042
|
+
_SSEEventPublisher.logger.info("Creating new SSEEventPublisher instance");
|
|
4043
|
+
_SSEEventPublisher.instance = new _SSEEventPublisher();
|
|
4044
|
+
_SSEEventPublisher.redisClient = RedisSSEClient.getInstance();
|
|
4045
|
+
}
|
|
4046
|
+
return _SSEEventPublisher.instance;
|
|
4047
|
+
}
|
|
4048
|
+
constructor() {
|
|
4049
|
+
}
|
|
4050
|
+
/**
|
|
4051
|
+
* Publish a program status change event for cache invalidation
|
|
4052
|
+
* This triggers React Query to refetch the program data
|
|
4053
|
+
*/
|
|
4054
|
+
static async publishProgramStatusEvent(programId, ruleId, language, userId, organizationId) {
|
|
4055
|
+
_SSEEventPublisher.logger.info("Publishing program status change event", {
|
|
4056
|
+
programId,
|
|
4057
|
+
ruleId,
|
|
4058
|
+
language,
|
|
4059
|
+
userId,
|
|
4060
|
+
organizationId
|
|
4061
|
+
});
|
|
4062
|
+
try {
|
|
4063
|
+
const event = createProgramStatusChangeEvent(ruleId, language);
|
|
4064
|
+
await _SSEEventPublisher.publishEvent(
|
|
4065
|
+
"program_status_change",
|
|
4066
|
+
[ruleId, language],
|
|
4067
|
+
event,
|
|
4068
|
+
userId ? [userId] : void 0
|
|
4069
|
+
);
|
|
4070
|
+
_SSEEventPublisher.logger.debug(
|
|
4071
|
+
"Successfully published program status change event",
|
|
4072
|
+
{ programId, ruleId, language }
|
|
4073
|
+
);
|
|
4074
|
+
} catch (error) {
|
|
4075
|
+
_SSEEventPublisher.logger.error(
|
|
4076
|
+
"Failed to publish program status change event",
|
|
4077
|
+
{
|
|
4078
|
+
programId,
|
|
4079
|
+
userId,
|
|
4080
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4081
|
+
}
|
|
4082
|
+
);
|
|
4083
|
+
throw error;
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
/**
|
|
4087
|
+
* Publish an assessment status change event for cache invalidation
|
|
4088
|
+
* This triggers React Query to refetch the assessment data
|
|
4089
|
+
*/
|
|
4090
|
+
static async publishAssessmentStatusEvent(ruleId, language, userId, organizationId) {
|
|
4091
|
+
_SSEEventPublisher.logger.info("Publishing assessment status change event", {
|
|
4092
|
+
ruleId,
|
|
4093
|
+
language,
|
|
4094
|
+
userId,
|
|
4095
|
+
organizationId
|
|
4096
|
+
});
|
|
4097
|
+
try {
|
|
4098
|
+
const event = createAssessmentStatusChangeEvent(ruleId, language);
|
|
4099
|
+
await _SSEEventPublisher.publishEvent(
|
|
4100
|
+
"assessment_status_change",
|
|
4101
|
+
[ruleId, language],
|
|
4102
|
+
event,
|
|
4103
|
+
userId ? [userId] : void 0
|
|
4104
|
+
);
|
|
4105
|
+
_SSEEventPublisher.logger.debug(
|
|
4106
|
+
"Successfully published assessment status change event",
|
|
4107
|
+
{ ruleId, language }
|
|
4108
|
+
);
|
|
4109
|
+
} catch (error) {
|
|
4110
|
+
_SSEEventPublisher.logger.error(
|
|
4111
|
+
"Failed to publish assessment status change event",
|
|
4112
|
+
{
|
|
4113
|
+
userId,
|
|
4114
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4115
|
+
}
|
|
4116
|
+
);
|
|
4117
|
+
throw error;
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
/**
|
|
4121
|
+
* Publish a detection heuristics updated event for cache invalidation
|
|
4122
|
+
* This triggers React Query to refetch the heuristics data
|
|
4123
|
+
*/
|
|
4124
|
+
static async publishDetectionHeuristicsUpdatedEvent(ruleId, language, detectionHeuristicsId, userId, organizationId) {
|
|
4125
|
+
_SSEEventPublisher.logger.info(
|
|
4126
|
+
"Publishing detection heuristics updated event",
|
|
4127
|
+
{
|
|
4128
|
+
ruleId,
|
|
4129
|
+
language,
|
|
4130
|
+
detectionHeuristicsId,
|
|
4131
|
+
userId,
|
|
4132
|
+
organizationId
|
|
4133
|
+
}
|
|
4134
|
+
);
|
|
4135
|
+
try {
|
|
4136
|
+
const event = createDetectionHeuristicsUpdatedEvent(
|
|
4137
|
+
ruleId,
|
|
4138
|
+
language,
|
|
4139
|
+
detectionHeuristicsId
|
|
4140
|
+
);
|
|
4141
|
+
await _SSEEventPublisher.publishEvent(
|
|
4142
|
+
"detection_heuristics_updated",
|
|
4143
|
+
[ruleId, language, detectionHeuristicsId],
|
|
4144
|
+
event,
|
|
4145
|
+
userId ? [userId] : void 0
|
|
4146
|
+
);
|
|
4147
|
+
_SSEEventPublisher.logger.debug(
|
|
4148
|
+
"Successfully published detection heuristics updated event",
|
|
4149
|
+
{ ruleId, language, detectionHeuristicsId }
|
|
4150
|
+
);
|
|
4151
|
+
} catch (error) {
|
|
4152
|
+
_SSEEventPublisher.logger.error(
|
|
4153
|
+
"Failed to publish detection heuristics updated event",
|
|
4154
|
+
{
|
|
4155
|
+
ruleId,
|
|
4156
|
+
language,
|
|
4157
|
+
detectionHeuristicsId,
|
|
4158
|
+
userId,
|
|
4159
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4160
|
+
}
|
|
4161
|
+
);
|
|
4162
|
+
throw error;
|
|
4163
|
+
}
|
|
4164
|
+
}
|
|
4165
|
+
/**
|
|
4166
|
+
* Publish an event to notify a user that their context (role or membership) changed
|
|
4167
|
+
* This should trigger a refetch of the /me route on the frontend
|
|
4168
|
+
*/
|
|
4169
|
+
static async publishUserContextChangeEvent(userId, organizationId, changeType, role) {
|
|
4170
|
+
_SSEEventPublisher.logger.info("Publishing user context change event", {
|
|
4171
|
+
userId,
|
|
4172
|
+
organizationId,
|
|
4173
|
+
changeType,
|
|
4174
|
+
role
|
|
4175
|
+
});
|
|
4176
|
+
try {
|
|
4177
|
+
const event = createUserContextChangeEvent(
|
|
4178
|
+
userId,
|
|
4179
|
+
organizationId,
|
|
4180
|
+
changeType,
|
|
4181
|
+
role
|
|
4182
|
+
);
|
|
4183
|
+
await _SSEEventPublisher.publishEvent("user_context_change", [], event, [
|
|
4184
|
+
userId
|
|
4185
|
+
]);
|
|
4186
|
+
_SSEEventPublisher.logger.debug(
|
|
4187
|
+
"Successfully published user context change event",
|
|
4188
|
+
{
|
|
4189
|
+
userId,
|
|
4190
|
+
organizationId,
|
|
4191
|
+
changeType
|
|
4192
|
+
}
|
|
4193
|
+
);
|
|
4194
|
+
} catch (error) {
|
|
4195
|
+
_SSEEventPublisher.logger.error(
|
|
4196
|
+
"Failed to publish user context change event",
|
|
4197
|
+
{
|
|
4198
|
+
userId,
|
|
4199
|
+
organizationId,
|
|
4200
|
+
changeType,
|
|
4201
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4202
|
+
}
|
|
4203
|
+
);
|
|
4204
|
+
throw error;
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
/**
|
|
4208
|
+
* Generic method to publish any SSE event type to Redis pub/sub
|
|
4209
|
+
*/
|
|
4210
|
+
static async publishEvent(eventType, params, data, targetUserIds) {
|
|
4211
|
+
_SSEEventPublisher.logger.info("Publishing SSE event", {
|
|
4212
|
+
eventType,
|
|
4213
|
+
params,
|
|
4214
|
+
targetUserIds: targetUserIds?.length || "all"
|
|
4215
|
+
});
|
|
4216
|
+
try {
|
|
4217
|
+
_SSEEventPublisher.getInstance();
|
|
4218
|
+
const redisClient = _SSEEventPublisher.redisClient;
|
|
4219
|
+
const message = createSSEEventMessage(
|
|
4220
|
+
eventType,
|
|
4221
|
+
params,
|
|
4222
|
+
data,
|
|
4223
|
+
targetUserIds
|
|
4224
|
+
);
|
|
4225
|
+
const serializedMessage = serializeSSERedisMessage(message);
|
|
4226
|
+
await redisClient.publish(SSE_REDIS_CHANNELS.EVENTS, serializedMessage);
|
|
4227
|
+
_SSEEventPublisher.logger.debug("Successfully published SSE event", {
|
|
4228
|
+
eventType,
|
|
4229
|
+
params
|
|
4230
|
+
});
|
|
4231
|
+
} catch (error) {
|
|
4232
|
+
_SSEEventPublisher.logger.error("Failed to publish SSE event", {
|
|
4233
|
+
eventType,
|
|
4234
|
+
params,
|
|
4235
|
+
error: error instanceof Error ? error.message : String(error)
|
|
4236
|
+
});
|
|
4237
|
+
throw error;
|
|
4238
|
+
}
|
|
4239
|
+
}
|
|
4240
|
+
};
|
|
4241
|
+
|
|
4242
|
+
// packages/node-utils/src/text/sectionMerge.ts
|
|
4243
|
+
function mergeSectionsIntoFileContent(existingContent, sections) {
|
|
4244
|
+
let result = existingContent;
|
|
4245
|
+
for (const section of sections) {
|
|
4246
|
+
const startMarker = `<!-- start: ${section.key} -->`;
|
|
4247
|
+
const endMarker = `<!-- end: ${section.key} -->`;
|
|
4248
|
+
const startIndex = result.indexOf(startMarker);
|
|
4249
|
+
const endIndex = result.indexOf(endMarker);
|
|
4250
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
4251
|
+
const before = result.substring(0, startIndex + startMarker.length);
|
|
4252
|
+
const after = result.substring(endIndex);
|
|
4253
|
+
result = `${before}
|
|
4254
|
+
${section.content}
|
|
4255
|
+
${after}`;
|
|
4256
|
+
} else {
|
|
4257
|
+
const sectionBlock = `${startMarker}
|
|
4258
|
+
${section.content}
|
|
4259
|
+
${endMarker}`;
|
|
4260
|
+
if (result.trim() === "") {
|
|
4261
|
+
result = sectionBlock;
|
|
4262
|
+
} else {
|
|
4263
|
+
result = result.endsWith("\n") ? `${result}${sectionBlock}
|
|
4264
|
+
` : `${result}
|
|
4265
|
+
${sectionBlock}
|
|
4266
|
+
`;
|
|
4267
|
+
}
|
|
4268
|
+
}
|
|
4269
|
+
}
|
|
4270
|
+
return result;
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
// packages/node-utils/src/dataSources/local.ts
|
|
4274
|
+
var import_typeorm = require("typeorm");
|
|
4275
|
+
var dataSource = makeDatasource();
|
|
4276
|
+
function makeDatasource() {
|
|
4277
|
+
try {
|
|
4278
|
+
return new import_typeorm.DataSource({
|
|
4279
|
+
type: "postgres",
|
|
4280
|
+
url: process.env["DATABASE_URL"],
|
|
4281
|
+
entities: [],
|
|
4282
|
+
migrations: []
|
|
4283
|
+
});
|
|
4284
|
+
} catch {
|
|
4285
|
+
return {};
|
|
4286
|
+
}
|
|
4287
|
+
}
|
|
4288
|
+
|
|
4289
|
+
// apps/cli/src/application/useCases/PullDataUseCase.ts
|
|
4290
|
+
var fs3 = __toESM(require("fs/promises"));
|
|
4291
|
+
var path3 = __toESM(require("path"));
|
|
4292
|
+
var PullDataUseCase = class {
|
|
4293
|
+
constructor(packmindGateway) {
|
|
4294
|
+
this.packmindGateway = packmindGateway;
|
|
4295
|
+
}
|
|
4296
|
+
async execute(command3) {
|
|
4297
|
+
const baseDirectory = command3.baseDirectory || process.cwd();
|
|
4298
|
+
const result = {
|
|
4299
|
+
filesCreated: 0,
|
|
4300
|
+
filesUpdated: 0,
|
|
4301
|
+
filesDeleted: 0,
|
|
4302
|
+
errors: []
|
|
4303
|
+
};
|
|
4304
|
+
const response = await this.packmindGateway.getPullData({
|
|
4305
|
+
packagesSlugs: command3.packagesSlugs
|
|
4306
|
+
});
|
|
4307
|
+
try {
|
|
4308
|
+
for (const file of response.fileUpdates.createOrUpdate) {
|
|
4309
|
+
try {
|
|
4310
|
+
await this.createOrUpdateFile(baseDirectory, file, result);
|
|
4311
|
+
} catch (error) {
|
|
4312
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4313
|
+
result.errors.push(
|
|
4314
|
+
`Failed to create/update ${file.path}: ${errorMsg}`
|
|
4315
|
+
);
|
|
4316
|
+
}
|
|
4317
|
+
}
|
|
4318
|
+
for (const file of response.fileUpdates.delete) {
|
|
4319
|
+
try {
|
|
4320
|
+
await this.deleteFile(baseDirectory, file.path, result);
|
|
4321
|
+
} catch (error) {
|
|
4322
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4323
|
+
result.errors.push(`Failed to delete ${file.path}: ${errorMsg}`);
|
|
4324
|
+
}
|
|
4325
|
+
}
|
|
4326
|
+
} catch (error) {
|
|
4327
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
4328
|
+
result.errors.push(`Failed to pull data: ${errorMsg}`);
|
|
4329
|
+
}
|
|
4330
|
+
return result;
|
|
4331
|
+
}
|
|
4332
|
+
async createOrUpdateFile(baseDirectory, file, result) {
|
|
4333
|
+
const fullPath = path3.join(baseDirectory, file.path);
|
|
4334
|
+
const directory = path3.dirname(fullPath);
|
|
4335
|
+
await fs3.mkdir(directory, { recursive: true });
|
|
4336
|
+
const fileExists = await this.fileExists(fullPath);
|
|
4337
|
+
if (file.content !== void 0) {
|
|
4338
|
+
await this.handleFullContentUpdate(
|
|
4339
|
+
fullPath,
|
|
4340
|
+
file.content,
|
|
4341
|
+
fileExists,
|
|
4342
|
+
result
|
|
4343
|
+
);
|
|
4344
|
+
} else if (file.sections !== void 0) {
|
|
4345
|
+
await this.handleSectionsUpdate(
|
|
4346
|
+
fullPath,
|
|
4347
|
+
file.sections,
|
|
4348
|
+
fileExists,
|
|
4349
|
+
result
|
|
4350
|
+
);
|
|
4351
|
+
}
|
|
4352
|
+
}
|
|
4353
|
+
async handleFullContentUpdate(fullPath, content, fileExists, result) {
|
|
4354
|
+
if (fileExists) {
|
|
4355
|
+
const existingContent = await fs3.readFile(fullPath, "utf-8");
|
|
4356
|
+
const commentMarker = this.extractCommentMarker(content);
|
|
4357
|
+
let finalContent;
|
|
4358
|
+
if (!commentMarker) {
|
|
4359
|
+
finalContent = content;
|
|
4360
|
+
} else {
|
|
4361
|
+
finalContent = this.mergeContentWithMarkers(
|
|
4362
|
+
existingContent,
|
|
4363
|
+
content,
|
|
4364
|
+
commentMarker
|
|
4365
|
+
);
|
|
4366
|
+
}
|
|
4367
|
+
await fs3.writeFile(fullPath, finalContent, "utf-8");
|
|
4368
|
+
result.filesUpdated++;
|
|
4369
|
+
} else {
|
|
4370
|
+
await fs3.writeFile(fullPath, content, "utf-8");
|
|
4371
|
+
result.filesCreated++;
|
|
4372
|
+
}
|
|
4373
|
+
}
|
|
4374
|
+
async handleSectionsUpdate(fullPath, sections, fileExists, result) {
|
|
4375
|
+
let currentContent = "";
|
|
4376
|
+
if (fileExists) {
|
|
4377
|
+
currentContent = await fs3.readFile(fullPath, "utf-8");
|
|
4378
|
+
}
|
|
4379
|
+
const mergedContent = mergeSectionsIntoFileContent(
|
|
4380
|
+
currentContent,
|
|
4381
|
+
sections
|
|
4382
|
+
);
|
|
4383
|
+
await fs3.writeFile(fullPath, mergedContent, "utf-8");
|
|
4384
|
+
if (fileExists) {
|
|
4385
|
+
result.filesUpdated++;
|
|
4386
|
+
} else {
|
|
4387
|
+
result.filesCreated++;
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
async deleteFile(baseDirectory, filePath, result) {
|
|
4391
|
+
const fullPath = path3.join(baseDirectory, filePath);
|
|
4392
|
+
const fileExists = await this.fileExists(fullPath);
|
|
4393
|
+
if (fileExists) {
|
|
4394
|
+
await fs3.unlink(fullPath);
|
|
4395
|
+
result.filesDeleted++;
|
|
4396
|
+
}
|
|
4397
|
+
}
|
|
4398
|
+
async fileExists(filePath) {
|
|
4399
|
+
try {
|
|
4400
|
+
await fs3.access(filePath);
|
|
4401
|
+
return true;
|
|
4402
|
+
} catch {
|
|
4403
|
+
return false;
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
/**
|
|
4407
|
+
* Extracts the comment marker from content if it's wrapped with HTML comments.
|
|
4408
|
+
* E.g., "<!-- start: Packmind recipes -->" returns "Packmind recipes"
|
|
4409
|
+
*/
|
|
4410
|
+
extractCommentMarker(content) {
|
|
4411
|
+
const startMarkerPattern = /<!--\s*start:\s*([^-]+?)\s*-->/;
|
|
4412
|
+
const match = content.match(startMarkerPattern);
|
|
4413
|
+
return match ? match[1].trim() : null;
|
|
4414
|
+
}
|
|
4415
|
+
/**
|
|
4416
|
+
* Merges new content with existing content using comment markers.
|
|
4417
|
+
* If the section exists, replaces it; otherwise, appends it.
|
|
4418
|
+
*/
|
|
4419
|
+
mergeContentWithMarkers(existingContent, newContent, commentMarker) {
|
|
4420
|
+
const startMarker = `<!-- start: ${commentMarker} -->`;
|
|
4421
|
+
const endMarker = `<!-- end: ${commentMarker} -->`;
|
|
4422
|
+
const newSectionPattern = new RegExp(
|
|
4423
|
+
`${this.escapeRegex(startMarker)}([\\s\\S]*?)${this.escapeRegex(endMarker)}`
|
|
4424
|
+
);
|
|
4425
|
+
const newSectionMatch = newContent.match(newSectionPattern);
|
|
4426
|
+
const newSectionContent = newSectionMatch ? newSectionMatch[1].trim() : newContent;
|
|
4427
|
+
const existingSectionPattern = new RegExp(
|
|
4428
|
+
`${this.escapeRegex(startMarker)}[\\s\\S]*?${this.escapeRegex(endMarker)}`,
|
|
4429
|
+
"g"
|
|
4430
|
+
);
|
|
4431
|
+
if (existingSectionPattern.test(existingContent)) {
|
|
4432
|
+
return existingContent.replace(
|
|
4433
|
+
existingSectionPattern,
|
|
4434
|
+
`${startMarker}
|
|
4435
|
+
${newSectionContent}
|
|
4436
|
+
${endMarker}`
|
|
4437
|
+
);
|
|
4438
|
+
} else {
|
|
4439
|
+
return `${existingContent}
|
|
4440
|
+
${startMarker}
|
|
4441
|
+
${newSectionContent}
|
|
4442
|
+
${endMarker}`;
|
|
4443
|
+
}
|
|
4444
|
+
}
|
|
4445
|
+
/**
|
|
4446
|
+
* Escapes special regex characters in a string
|
|
4447
|
+
*/
|
|
4448
|
+
escapeRegex(str) {
|
|
4449
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4450
|
+
}
|
|
4451
|
+
};
|
|
4452
|
+
|
|
4453
|
+
// apps/cli/src/application/useCases/ListPackagesUseCase.ts
|
|
4454
|
+
var ListPackagesUseCase = class {
|
|
4455
|
+
constructor(packmindGateway) {
|
|
4456
|
+
this.packmindGateway = packmindGateway;
|
|
4457
|
+
}
|
|
4458
|
+
async execute() {
|
|
4459
|
+
return this.packmindGateway.listPackages({});
|
|
4460
|
+
}
|
|
4461
|
+
};
|
|
4462
|
+
|
|
4463
|
+
// apps/cli/src/application/useCases/GetPackageSummaryUseCase.ts
|
|
4464
|
+
var GetPackageSummaryUseCase = class {
|
|
4465
|
+
constructor(gateway) {
|
|
4466
|
+
this.gateway = gateway;
|
|
4467
|
+
}
|
|
4468
|
+
async execute(command3) {
|
|
4469
|
+
return this.gateway.getPackageSummary(command3);
|
|
4470
|
+
}
|
|
4471
|
+
};
|
|
4472
|
+
|
|
4473
|
+
// apps/cli/src/PackmindCliHexaFactory.ts
|
|
4474
|
+
var PackmindCliHexaFactory = class {
|
|
4475
|
+
constructor(logger2) {
|
|
4476
|
+
this.logger = logger2;
|
|
4477
|
+
this.repositories = {
|
|
4478
|
+
packmindGateway: new PackmindGateway(
|
|
4479
|
+
process.env.PACKMIND_API_KEY_V3 || ""
|
|
3293
4480
|
)
|
|
3294
4481
|
};
|
|
3295
4482
|
this.services = {
|
|
@@ -3308,16 +4495,20 @@ var PackmindCliHexaFactory = class {
|
|
|
3308
4495
|
this.repositories,
|
|
3309
4496
|
this.logger
|
|
3310
4497
|
),
|
|
3311
|
-
pullData: new PullDataUseCase(this.repositories.packmindGateway)
|
|
4498
|
+
pullData: new PullDataUseCase(this.repositories.packmindGateway),
|
|
4499
|
+
listPackages: new ListPackagesUseCase(this.repositories.packmindGateway),
|
|
4500
|
+
getPackageBySlug: new GetPackageSummaryUseCase(
|
|
4501
|
+
this.repositories.packmindGateway
|
|
4502
|
+
)
|
|
3312
4503
|
};
|
|
3313
4504
|
}
|
|
3314
4505
|
};
|
|
3315
4506
|
|
|
3316
4507
|
// apps/cli/src/PackmindCliHexa.ts
|
|
3317
|
-
var
|
|
4508
|
+
var origin9 = "PackmindCliHexa";
|
|
3318
4509
|
var PackmindCliHexa = class {
|
|
3319
|
-
constructor(
|
|
3320
|
-
this.logger =
|
|
4510
|
+
constructor(logger2 = new PackmindLogger(origin9)) {
|
|
4511
|
+
this.logger = logger2;
|
|
3321
4512
|
try {
|
|
3322
4513
|
this.hexa = new PackmindCliHexaFactory(this.logger);
|
|
3323
4514
|
} catch (error) {
|
|
@@ -3349,6 +4540,12 @@ var PackmindCliHexa = class {
|
|
|
3349
4540
|
async pullData(command3) {
|
|
3350
4541
|
return this.hexa.useCases.pullData.execute(command3);
|
|
3351
4542
|
}
|
|
4543
|
+
async listPackages(command3) {
|
|
4544
|
+
return this.hexa.useCases.listPackages.execute(command3);
|
|
4545
|
+
}
|
|
4546
|
+
async getPackageBySlug(command3) {
|
|
4547
|
+
return this.hexa.useCases.getPackageBySlug.execute(command3);
|
|
4548
|
+
}
|
|
3352
4549
|
};
|
|
3353
4550
|
|
|
3354
4551
|
// apps/cli/src/infra/repositories/IDELintLogger.ts
|
|
@@ -3465,7 +4662,7 @@ var lintCommand = (0, import_cmd_ts.command)({
|
|
|
3465
4662
|
description: "Enable debug logging"
|
|
3466
4663
|
})
|
|
3467
4664
|
},
|
|
3468
|
-
handler: async ({ path: path5, draft, rule, debug, language, logger }) => {
|
|
4665
|
+
handler: async ({ path: path5, draft, rule, debug, language, logger: logger2 }) => {
|
|
3469
4666
|
if (draft && !rule) {
|
|
3470
4667
|
throw new Error("option --rule is required to use --draft mode");
|
|
3471
4668
|
}
|
|
@@ -3482,7 +4679,7 @@ var lintCommand = (0, import_cmd_ts.command)({
|
|
|
3482
4679
|
ruleId: rule?.ruleId,
|
|
3483
4680
|
language
|
|
3484
4681
|
});
|
|
3485
|
-
(
|
|
4682
|
+
(logger2 === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
|
|
3486
4683
|
const durationSeconds = (Date.now() - startedAt) / 1e3;
|
|
3487
4684
|
console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
|
|
3488
4685
|
if (violations.length > 0) {
|
|
@@ -3556,15 +4753,113 @@ var path4 = __toESM(require("path"));
|
|
|
3556
4753
|
var import_cmd_ts2 = require("cmd-ts");
|
|
3557
4754
|
var pullCommand = (0, import_cmd_ts2.command)({
|
|
3558
4755
|
name: "pull",
|
|
3559
|
-
description: "Pull recipes and standards from
|
|
3560
|
-
args: {
|
|
3561
|
-
|
|
3562
|
-
|
|
4756
|
+
description: "Pull recipes and standards from specified packages and save them to the current directory",
|
|
4757
|
+
args: {
|
|
4758
|
+
list: (0, import_cmd_ts2.flag)({
|
|
4759
|
+
long: "list",
|
|
4760
|
+
description: "List available packages"
|
|
4761
|
+
}),
|
|
4762
|
+
show: (0, import_cmd_ts2.option)({
|
|
4763
|
+
type: import_cmd_ts2.string,
|
|
4764
|
+
long: "show",
|
|
4765
|
+
description: "Show details of a specific package",
|
|
4766
|
+
defaultValue: () => ""
|
|
4767
|
+
}),
|
|
4768
|
+
packagesSlugs: (0, import_cmd_ts2.restPositionals)({
|
|
4769
|
+
type: import_cmd_ts2.string,
|
|
4770
|
+
displayName: "packages",
|
|
4771
|
+
description: "Package slugs to pull content from (e.g., backend frontend)"
|
|
4772
|
+
})
|
|
4773
|
+
},
|
|
4774
|
+
handler: async ({ list, show, packagesSlugs }) => {
|
|
3563
4775
|
const packmindLogger = new PackmindLogger("PackmindCLI", "info" /* INFO */);
|
|
3564
4776
|
const packmindCliHexa = new PackmindCliHexa(packmindLogger);
|
|
4777
|
+
if (list) {
|
|
4778
|
+
try {
|
|
4779
|
+
console.log("Fetching available packages...\n");
|
|
4780
|
+
const packages = await packmindCliHexa.listPackages({});
|
|
4781
|
+
if (packages.length === 0) {
|
|
4782
|
+
console.log("No packages found.");
|
|
4783
|
+
process.exit(0);
|
|
4784
|
+
}
|
|
4785
|
+
console.log("Available packages:");
|
|
4786
|
+
packages.forEach((pkg) => {
|
|
4787
|
+
console.log(` - ${pkg.slug}: ${pkg.description || pkg.name}`);
|
|
4788
|
+
});
|
|
4789
|
+
process.exit(0);
|
|
4790
|
+
} catch (error) {
|
|
4791
|
+
console.error("\n\u274C Failed to list packages:");
|
|
4792
|
+
if (error instanceof Error) {
|
|
4793
|
+
console.error(` ${error.message}`);
|
|
4794
|
+
} else {
|
|
4795
|
+
console.error(` ${String(error)}`);
|
|
4796
|
+
}
|
|
4797
|
+
process.exit(1);
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
if (show) {
|
|
4801
|
+
try {
|
|
4802
|
+
console.log(`Fetching package details for '${show}'...
|
|
4803
|
+
`);
|
|
4804
|
+
const pkg = await packmindCliHexa.getPackageBySlug({ slug: show });
|
|
4805
|
+
console.log(`${pkg.name} (${pkg.slug}):
|
|
4806
|
+
`);
|
|
4807
|
+
if (pkg.description) {
|
|
4808
|
+
console.log(`${pkg.description}
|
|
4809
|
+
`);
|
|
4810
|
+
}
|
|
4811
|
+
if (pkg.standards && pkg.standards.length > 0) {
|
|
4812
|
+
console.log("Standards:");
|
|
4813
|
+
pkg.standards.forEach((standard) => {
|
|
4814
|
+
if (standard.summary) {
|
|
4815
|
+
console.log(` - ${standard.name}: ${standard.summary}`);
|
|
4816
|
+
} else {
|
|
4817
|
+
console.log(` - ${standard.name}`);
|
|
4818
|
+
}
|
|
4819
|
+
});
|
|
4820
|
+
console.log("");
|
|
4821
|
+
}
|
|
4822
|
+
if (pkg.recipes && pkg.recipes.length > 0) {
|
|
4823
|
+
console.log("Recipes:");
|
|
4824
|
+
pkg.recipes.forEach((recipe) => {
|
|
4825
|
+
if (recipe.summary) {
|
|
4826
|
+
console.log(` - ${recipe.name}: ${recipe.summary}`);
|
|
4827
|
+
} else {
|
|
4828
|
+
console.log(` - ${recipe.name}`);
|
|
4829
|
+
}
|
|
4830
|
+
});
|
|
4831
|
+
console.log("");
|
|
4832
|
+
}
|
|
4833
|
+
process.exit(0);
|
|
4834
|
+
} catch (error) {
|
|
4835
|
+
console.error("\n\u274C Failed to fetch package details:");
|
|
4836
|
+
if (error instanceof Error) {
|
|
4837
|
+
console.error(` ${error.message}`);
|
|
4838
|
+
} else {
|
|
4839
|
+
console.error(` ${String(error)}`);
|
|
4840
|
+
}
|
|
4841
|
+
process.exit(1);
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
if (packagesSlugs.length === 0) {
|
|
4845
|
+
console.log("Usage: packmind-cli pull <package-slug> [package-slug...]");
|
|
4846
|
+
console.log(" packmind-cli pull --list");
|
|
4847
|
+
console.log("");
|
|
4848
|
+
console.log("Examples:");
|
|
4849
|
+
console.log(" packmind-cli pull backend");
|
|
4850
|
+
console.log(" packmind-cli pull backend frontend");
|
|
4851
|
+
console.log(" packmind-cli pull --list # Show available packages");
|
|
4852
|
+
console.log("");
|
|
4853
|
+
console.log("Pull recipes and standards from the specified packages.");
|
|
4854
|
+
process.exit(0);
|
|
4855
|
+
}
|
|
4856
|
+
console.log(
|
|
4857
|
+
`Pulling content from packages: ${packagesSlugs.join(", ")}...`
|
|
4858
|
+
);
|
|
3565
4859
|
try {
|
|
3566
4860
|
const result = await packmindCliHexa.pullData({
|
|
3567
|
-
baseDirectory: process.cwd()
|
|
4861
|
+
baseDirectory: process.cwd(),
|
|
4862
|
+
packagesSlugs
|
|
3568
4863
|
});
|
|
3569
4864
|
console.log("\n\u2705 Pull completed successfully!");
|
|
3570
4865
|
console.log(` Files created: ${result.filesCreated}`);
|
|
@@ -3579,15 +4874,37 @@ var pullCommand = (0, import_cmd_ts2.command)({
|
|
|
3579
4874
|
}
|
|
3580
4875
|
} catch (error) {
|
|
3581
4876
|
console.error("\n\u274C Failed to pull content:");
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
4877
|
+
if (error instanceof Error) {
|
|
4878
|
+
const errorObj = error;
|
|
4879
|
+
if (errorObj.statusCode === 404) {
|
|
4880
|
+
console.error(` ${errorObj.message}`);
|
|
4881
|
+
console.error(
|
|
4882
|
+
"\n\u{1F4A1} Use `packmind-cli pull --list` to show available packages"
|
|
4883
|
+
);
|
|
4884
|
+
} else {
|
|
4885
|
+
console.error(` ${errorObj.message}`);
|
|
4886
|
+
const apiErrorObj = error;
|
|
4887
|
+
if (apiErrorObj.response?.data?.message) {
|
|
4888
|
+
console.error(`
|
|
4889
|
+
Details: ${apiErrorObj.response.data.message}`);
|
|
4890
|
+
}
|
|
4891
|
+
console.error("\n\u{1F4A1} Troubleshooting tips:");
|
|
4892
|
+
console.error(" - Verify that the package slugs are correct");
|
|
4893
|
+
console.error(
|
|
4894
|
+
" - Check that the packages exist in your organization"
|
|
4895
|
+
);
|
|
4896
|
+
console.error(" - Ensure you have the correct API key configured");
|
|
4897
|
+
}
|
|
4898
|
+
} else {
|
|
4899
|
+
console.error(` ${String(error)}`);
|
|
4900
|
+
}
|
|
3585
4901
|
process.exit(1);
|
|
3586
4902
|
}
|
|
3587
4903
|
}
|
|
3588
4904
|
});
|
|
3589
4905
|
|
|
3590
4906
|
// apps/cli/src/main.ts
|
|
4907
|
+
var { version: CLI_VERSION } = require_package();
|
|
3591
4908
|
function findEnvFile() {
|
|
3592
4909
|
let currentDir = process.cwd();
|
|
3593
4910
|
const startDir = currentDir;
|
|
@@ -3626,6 +4943,11 @@ if (hasEmbeddedWasmFiles()) {
|
|
|
3626
4943
|
} catch {
|
|
3627
4944
|
}
|
|
3628
4945
|
}
|
|
4946
|
+
var args = process.argv.slice(2);
|
|
4947
|
+
if (args.includes("--version") || args.includes("-v")) {
|
|
4948
|
+
console.log(`packmind-cli version ${CLI_VERSION}`);
|
|
4949
|
+
process.exit(0);
|
|
4950
|
+
}
|
|
3629
4951
|
var app = (0, import_cmd_ts3.subcommands)({
|
|
3630
4952
|
name: "packmind-cli",
|
|
3631
4953
|
description: "Packmind CLI tool",
|
|
@@ -3634,7 +4956,7 @@ var app = (0, import_cmd_ts3.subcommands)({
|
|
|
3634
4956
|
pull: pullCommand
|
|
3635
4957
|
}
|
|
3636
4958
|
});
|
|
3637
|
-
(0, import_cmd_ts3.run)(app,
|
|
4959
|
+
(0, import_cmd_ts3.run)(app, args).catch((error) => {
|
|
3638
4960
|
console.error(import_chalk2.default.bgRed.bold("packmind-cli"), import_chalk2.default.red(error.message));
|
|
3639
4961
|
process.exit(1);
|
|
3640
4962
|
});
|