@packmind/cli 0.2.5
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 +4084 -0
- package/package.json +51 -0
- package/tree-sitter-c_sharp.wasm +0 -0
- package/tree-sitter-cpp.wasm +0 -0
- package/tree-sitter-go.wasm +0 -0
- package/tree-sitter-html.wasm +0 -0
- package/tree-sitter-java.wasm +0 -0
- package/tree-sitter-javascript.wasm +0 -0
- package/tree-sitter-json.wasm +0 -0
- package/tree-sitter-kotlin.wasm +0 -0
- package/tree-sitter-php.wasm +0 -0
- package/tree-sitter-php_only.wasm +0 -0
- package/tree-sitter-python.wasm +0 -0
- package/tree-sitter-ruby.wasm +0 -0
- package/tree-sitter-scss.wasm +0 -0
- package/tree-sitter-swift.wasm +0 -0
- package/tree-sitter-tsx.wasm +0 -0
- package/tree-sitter-typescript.wasm +0 -0
- package/tree-sitter-yaml.wasm +0 -0
- package/tree-sitter.wasm +0 -0
package/main.cjs
ADDED
|
@@ -0,0 +1,4084 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __copyProps = (to, from, except, desc) => {
|
|
8
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
9
|
+
for (let key of __getOwnPropNames(from))
|
|
10
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
11
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
12
|
+
}
|
|
13
|
+
return to;
|
|
14
|
+
};
|
|
15
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
16
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
17
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
18
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
19
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
20
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
21
|
+
mod
|
|
22
|
+
));
|
|
23
|
+
|
|
24
|
+
// apps/cli/src/main.ts
|
|
25
|
+
var import_chalk2 = __toESM(require("chalk"));
|
|
26
|
+
var import_cmd_ts2 = require("cmd-ts");
|
|
27
|
+
|
|
28
|
+
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
29
|
+
var import_cmd_ts = require("cmd-ts");
|
|
30
|
+
|
|
31
|
+
// packages/shared/src/ai/prompts/OpenAIService.ts
|
|
32
|
+
var import_openai = __toESM(require("openai"));
|
|
33
|
+
|
|
34
|
+
// packages/shared/src/config/infra/Infisical/InfisicalConfig.ts
|
|
35
|
+
var import_sdk = require("@infisical/sdk");
|
|
36
|
+
|
|
37
|
+
// packages/shared/src/logger/PackmindLogger.ts
|
|
38
|
+
var import_winston = __toESM(require("winston"));
|
|
39
|
+
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
40
|
+
LogLevel2["SILENT"] = "silent";
|
|
41
|
+
LogLevel2["ERROR"] = "error";
|
|
42
|
+
LogLevel2["WARN"] = "warn";
|
|
43
|
+
LogLevel2["INFO"] = "info";
|
|
44
|
+
LogLevel2["HTTP"] = "http";
|
|
45
|
+
LogLevel2["VERBOSE"] = "verbose";
|
|
46
|
+
LogLevel2["DEBUG"] = "debug";
|
|
47
|
+
LogLevel2["SILLY"] = "silly";
|
|
48
|
+
return LogLevel2;
|
|
49
|
+
})(LogLevel || {});
|
|
50
|
+
var PackmindLogger = class {
|
|
51
|
+
constructor(name, level = "info" /* INFO */) {
|
|
52
|
+
this.name = name;
|
|
53
|
+
const envLogLevel = process.env["PACKMIND_LOG_LEVEL"];
|
|
54
|
+
let finalLevel = level;
|
|
55
|
+
if (envLogLevel && Object.values(LogLevel).includes(envLogLevel)) {
|
|
56
|
+
finalLevel = envLogLevel;
|
|
57
|
+
}
|
|
58
|
+
this.currentLevel = finalLevel;
|
|
59
|
+
if (finalLevel !== "silent" /* SILENT */) {
|
|
60
|
+
this.logger = import_winston.default.createLogger({
|
|
61
|
+
level: finalLevel,
|
|
62
|
+
format: import_winston.default.format.combine(
|
|
63
|
+
import_winston.default.format.timestamp(),
|
|
64
|
+
import_winston.default.format.errors({ stack: true }),
|
|
65
|
+
import_winston.default.format.label({ label: this.name }),
|
|
66
|
+
import_winston.default.format.json()
|
|
67
|
+
),
|
|
68
|
+
transports: [
|
|
69
|
+
new import_winston.default.transports.Console({
|
|
70
|
+
format: import_winston.default.format.combine(
|
|
71
|
+
import_winston.default.format.colorize(),
|
|
72
|
+
import_winston.default.format.printf(
|
|
73
|
+
({ timestamp, level: level2, message, label, ...meta }) => {
|
|
74
|
+
const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta)}` : "";
|
|
75
|
+
return `${timestamp} [${label}] ${level2}: ${message}${metaStr}`;
|
|
76
|
+
}
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
})
|
|
80
|
+
]
|
|
81
|
+
});
|
|
82
|
+
} else {
|
|
83
|
+
this.logger = null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
error(message, meta) {
|
|
87
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
88
|
+
this.logger.error(message, meta);
|
|
89
|
+
}
|
|
90
|
+
warn(message, meta) {
|
|
91
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
92
|
+
this.logger.warn(message, meta);
|
|
93
|
+
}
|
|
94
|
+
info(message, meta) {
|
|
95
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
96
|
+
this.logger.info(message, meta);
|
|
97
|
+
}
|
|
98
|
+
http(message, meta) {
|
|
99
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
100
|
+
this.logger.http(message, meta);
|
|
101
|
+
}
|
|
102
|
+
verbose(message, meta) {
|
|
103
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
104
|
+
this.logger.verbose(message, meta);
|
|
105
|
+
}
|
|
106
|
+
debug(message, meta) {
|
|
107
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
108
|
+
this.logger.debug(message, meta);
|
|
109
|
+
}
|
|
110
|
+
silly(message, meta) {
|
|
111
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
112
|
+
this.logger.silly(message, meta);
|
|
113
|
+
}
|
|
114
|
+
log(level, message, meta) {
|
|
115
|
+
if (this.currentLevel === "silent" /* SILENT */ || !this.logger) return;
|
|
116
|
+
this.logger.log(level, message, meta);
|
|
117
|
+
}
|
|
118
|
+
setLevel(level) {
|
|
119
|
+
this.currentLevel = level;
|
|
120
|
+
if (level !== "silent" /* SILENT */ && this.logger) {
|
|
121
|
+
this.logger.level = level;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
getName() {
|
|
125
|
+
return this.name;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// packages/shared/src/config/infra/Infisical/InfisicalConfig.ts
|
|
130
|
+
var origin = "InfisicalConfig";
|
|
131
|
+
var InfisicalConfig = class {
|
|
132
|
+
constructor(clientId, clientSecret, env, projectId, logger = new PackmindLogger(origin)) {
|
|
133
|
+
this.clientId = clientId;
|
|
134
|
+
this.clientSecret = clientSecret;
|
|
135
|
+
this.env = env;
|
|
136
|
+
this.projectId = projectId;
|
|
137
|
+
this.logger = logger;
|
|
138
|
+
this.logger.info("Initializing InfisicalConfig", { env, projectId });
|
|
139
|
+
try {
|
|
140
|
+
this.client = new import_sdk.InfisicalSDK({
|
|
141
|
+
siteUrl: "https://eu.infisical.com"
|
|
142
|
+
// Optional, defaults to https://app.infisical.com
|
|
143
|
+
});
|
|
144
|
+
this.logger.info("InfisicalSDK client created successfully", {
|
|
145
|
+
siteUrl: "https://eu.infisical.com"
|
|
146
|
+
});
|
|
147
|
+
} catch (error) {
|
|
148
|
+
this.logger.error("Failed to create InfisicalSDK client", {
|
|
149
|
+
error: error instanceof Error ? error.message : String(error)
|
|
150
|
+
});
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async initClient() {
|
|
155
|
+
this.logger.info("Initializing Infisical client authentication");
|
|
156
|
+
try {
|
|
157
|
+
this.logger.debug("Authenticating with Infisical using universal auth");
|
|
158
|
+
await this.client.auth().universalAuth.login({
|
|
159
|
+
clientId: this.clientId,
|
|
160
|
+
clientSecret: this.clientSecret
|
|
161
|
+
});
|
|
162
|
+
this.logger.info("Infisical client authenticated successfully");
|
|
163
|
+
} catch (error) {
|
|
164
|
+
this.logger.error("Failed to authenticate Infisical client", {
|
|
165
|
+
error: error instanceof Error ? error.message : String(error)
|
|
166
|
+
});
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async getValue(secretName) {
|
|
171
|
+
this.logger.info("Retrieving secret from Infisical", {
|
|
172
|
+
secretName,
|
|
173
|
+
env: this.env,
|
|
174
|
+
projectId: this.projectId
|
|
175
|
+
});
|
|
176
|
+
try {
|
|
177
|
+
this.logger.debug("Fetching secret from Infisical API", { secretName });
|
|
178
|
+
const nameSecret = await this.client.secrets().getSecret({
|
|
179
|
+
projectId: this.projectId,
|
|
180
|
+
environment: this.env,
|
|
181
|
+
secretName
|
|
182
|
+
});
|
|
183
|
+
if (nameSecret?.secretValue) {
|
|
184
|
+
this.logger.info("Secret retrieved from Infisical successfully", {
|
|
185
|
+
secretName
|
|
186
|
+
});
|
|
187
|
+
return nameSecret.secretValue;
|
|
188
|
+
} else {
|
|
189
|
+
this.logger.warn("Secret not found or has no value in Infisical", {
|
|
190
|
+
secretName
|
|
191
|
+
});
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
} catch (error) {
|
|
195
|
+
this.logger.warn("Failed to retrieve secret from Infisical", {
|
|
196
|
+
secretName,
|
|
197
|
+
env: this.env,
|
|
198
|
+
projectId: this.projectId,
|
|
199
|
+
error: error instanceof Error ? error.message : String(error)
|
|
200
|
+
});
|
|
201
|
+
throw error;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
// packages/shared/src/config/config/Configuration.ts
|
|
207
|
+
var origin2 = "Configuration";
|
|
208
|
+
var Configuration = class _Configuration {
|
|
209
|
+
constructor() {
|
|
210
|
+
this.initialized = false;
|
|
211
|
+
this.initializationPromise = null;
|
|
212
|
+
}
|
|
213
|
+
static {
|
|
214
|
+
this.logger = new PackmindLogger(
|
|
215
|
+
origin2,
|
|
216
|
+
"info" /* INFO */
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
static getInstance(logger) {
|
|
220
|
+
if (logger) {
|
|
221
|
+
_Configuration.logger = logger;
|
|
222
|
+
}
|
|
223
|
+
_Configuration.logger.debug("Getting Configuration instance");
|
|
224
|
+
if (!_Configuration.instance) {
|
|
225
|
+
_Configuration.logger.info("Creating new Configuration instance");
|
|
226
|
+
_Configuration.instance = new _Configuration();
|
|
227
|
+
}
|
|
228
|
+
return _Configuration.instance;
|
|
229
|
+
}
|
|
230
|
+
async initialize(env) {
|
|
231
|
+
if (this.initialized) {
|
|
232
|
+
_Configuration.logger.debug("Configuration already initialized, skipping");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (this.initializationPromise) {
|
|
236
|
+
_Configuration.logger.debug(
|
|
237
|
+
"Configuration initialization already in progress, waiting for completion"
|
|
238
|
+
);
|
|
239
|
+
await this.initializationPromise;
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
this.initializationPromise = this.performInitialization(env);
|
|
243
|
+
try {
|
|
244
|
+
await this.initializationPromise;
|
|
245
|
+
} finally {
|
|
246
|
+
this.initializationPromise = null;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async performInitialization(env) {
|
|
250
|
+
_Configuration.logger.info("Initializing Configuration");
|
|
251
|
+
try {
|
|
252
|
+
const configurationMode = env["CONFIGURATION"]?.toLowerCase();
|
|
253
|
+
_Configuration.logger.debug("Configuration mode detected", {
|
|
254
|
+
mode: configurationMode
|
|
255
|
+
});
|
|
256
|
+
if (configurationMode === "infisical") {
|
|
257
|
+
_Configuration.logger.info("Initializing Infisical configuration");
|
|
258
|
+
const clientId = env["INFISICAL_CLIENT_ID"];
|
|
259
|
+
const clientSecret = env["INFISICAL_CLIENT_SECRET"];
|
|
260
|
+
const infisicalEnv = env["INFISICAL_ENV"];
|
|
261
|
+
const projectId = env["INFISICAL_PROJECT_ID"];
|
|
262
|
+
if (!clientId || !clientSecret || !infisicalEnv || !projectId) {
|
|
263
|
+
_Configuration.logger.error("Infisical configuration is incomplete", {
|
|
264
|
+
hasClientId: !!clientId,
|
|
265
|
+
hasClientSecret: !!clientSecret,
|
|
266
|
+
hasInfisicalEnv: !!infisicalEnv,
|
|
267
|
+
hasProjectId: !!projectId
|
|
268
|
+
});
|
|
269
|
+
throw new Error(
|
|
270
|
+
"Infisical configuration is incomplete. Please set INFISICAL_CLIENT_ID, INFISICAL_CLIENT_SECRET, INFISICAL_ENV, and INFISICAL_PROJECT_ID environment variables."
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
this.infisicalConfig = new InfisicalConfig(
|
|
274
|
+
clientId,
|
|
275
|
+
clientSecret,
|
|
276
|
+
infisicalEnv,
|
|
277
|
+
projectId
|
|
278
|
+
);
|
|
279
|
+
_Configuration.logger.debug("Initializing Infisical client");
|
|
280
|
+
await this.infisicalConfig.initClient();
|
|
281
|
+
_Configuration.logger.info(
|
|
282
|
+
"Infisical configuration initialized successfully"
|
|
283
|
+
);
|
|
284
|
+
} else {
|
|
285
|
+
_Configuration.logger.info(
|
|
286
|
+
"Using environment variables only (no Infisical)"
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
this.initialized = true;
|
|
290
|
+
_Configuration.logger.info("Configuration initialization completed");
|
|
291
|
+
} catch (error) {
|
|
292
|
+
_Configuration.logger.error("Failed to initialize Configuration", {
|
|
293
|
+
error: error instanceof Error ? error.message : String(error)
|
|
294
|
+
});
|
|
295
|
+
throw error;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
static async getConfigWithDefault(key, defaultValue) {
|
|
299
|
+
const value = await _Configuration.getConfig(key);
|
|
300
|
+
return value ?? defaultValue;
|
|
301
|
+
}
|
|
302
|
+
static async getConfig(key, env = process.env, logger) {
|
|
303
|
+
if (logger) {
|
|
304
|
+
_Configuration.logger = logger;
|
|
305
|
+
}
|
|
306
|
+
_Configuration.logger.info("Getting configuration value", { key });
|
|
307
|
+
try {
|
|
308
|
+
const instance = _Configuration.getInstance();
|
|
309
|
+
await instance.initialize(env);
|
|
310
|
+
const envValue = env[key];
|
|
311
|
+
if (envValue) {
|
|
312
|
+
_Configuration.logger.debug(
|
|
313
|
+
"Configuration value found in environment variables",
|
|
314
|
+
{ key }
|
|
315
|
+
);
|
|
316
|
+
return envValue;
|
|
317
|
+
}
|
|
318
|
+
if (instance.infisicalConfig) {
|
|
319
|
+
_Configuration.logger.debug(
|
|
320
|
+
"Checking Infisical for configuration value",
|
|
321
|
+
{ key }
|
|
322
|
+
);
|
|
323
|
+
const infisicalValue = await instance.infisicalConfig.getValue(key);
|
|
324
|
+
if (infisicalValue) {
|
|
325
|
+
_Configuration.logger.debug("Configuration value found in Infisical", {
|
|
326
|
+
key
|
|
327
|
+
});
|
|
328
|
+
return infisicalValue;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
_Configuration.logger.warn("Configuration value not found", { key });
|
|
332
|
+
return null;
|
|
333
|
+
} catch (error) {
|
|
334
|
+
_Configuration.logger.warn("Failed to get configuration value", {
|
|
335
|
+
key,
|
|
336
|
+
error: error instanceof Error ? error.message : String(error)
|
|
337
|
+
});
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
// packages/shared/src/cache/Cache.ts
|
|
344
|
+
var import_ioredis = __toESM(require("ioredis"));
|
|
345
|
+
var origin3 = "Cache";
|
|
346
|
+
var Cache = class _Cache {
|
|
347
|
+
constructor() {
|
|
348
|
+
this.initialized = false;
|
|
349
|
+
this.connectionConfig = {
|
|
350
|
+
host: "redis",
|
|
351
|
+
port: 6379,
|
|
352
|
+
maxRetriesPerRequest: 3
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
static {
|
|
356
|
+
this.logger = new PackmindLogger(
|
|
357
|
+
origin3,
|
|
358
|
+
"info" /* INFO */
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
static {
|
|
362
|
+
// Default cache expiration time in seconds (5 minutes)
|
|
363
|
+
this.DEFAULT_EXPIRATION_SECONDS = 300;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Get the singleton instance of Cache
|
|
367
|
+
*/
|
|
368
|
+
static getInstance() {
|
|
369
|
+
_Cache.logger.debug("Getting Cache instance");
|
|
370
|
+
if (!_Cache.instance) {
|
|
371
|
+
_Cache.logger.info("Creating new Cache instance");
|
|
372
|
+
_Cache.instance = new _Cache();
|
|
373
|
+
}
|
|
374
|
+
return _Cache.instance;
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Initialize the Redis client with configuration
|
|
378
|
+
* This should be called during application startup
|
|
379
|
+
*/
|
|
380
|
+
async initialize() {
|
|
381
|
+
if (this.initialized) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
_Cache.logger.info("Initializing Redis cache client");
|
|
385
|
+
try {
|
|
386
|
+
const redisUri = await Configuration.getConfig("REDIS_URI");
|
|
387
|
+
if (!redisUri) {
|
|
388
|
+
throw new Error("REDIS_URI configuration is required");
|
|
389
|
+
}
|
|
390
|
+
_Cache.logger.info("Using REDIS_URI configuration");
|
|
391
|
+
this.client = new import_ioredis.default(redisUri, {
|
|
392
|
+
maxRetriesPerRequest: 3
|
|
393
|
+
});
|
|
394
|
+
this.client.on("error", (error) => {
|
|
395
|
+
_Cache.logger.error("Redis cache client error", {
|
|
396
|
+
error: error instanceof Error ? error.message : String(error)
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
this.client.on("connect", () => {
|
|
400
|
+
_Cache.logger.info("Redis cache client connected successfully");
|
|
401
|
+
});
|
|
402
|
+
this.initialized = true;
|
|
403
|
+
_Cache.logger.info("Cache initialization completed");
|
|
404
|
+
} catch (error) {
|
|
405
|
+
_Cache.logger.error("Failed to initialize cache", {
|
|
406
|
+
error: error instanceof Error ? error.message : String(error)
|
|
407
|
+
});
|
|
408
|
+
throw new Error(
|
|
409
|
+
`Cache initialization failed: ${error instanceof Error ? error.message : String(error)}`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* Set a value in the cache with optional expiration
|
|
415
|
+
* @param key - The cache key
|
|
416
|
+
* @param value - The value to cache (will be JSON serialized)
|
|
417
|
+
* @param expirationSeconds - Expiration time in seconds (default: 300s)
|
|
418
|
+
*/
|
|
419
|
+
async set(key, value, expirationSeconds = _Cache.DEFAULT_EXPIRATION_SECONDS) {
|
|
420
|
+
if (!this.initialized || !this.client) {
|
|
421
|
+
_Cache.logger.warn("Cache not initialized, skipping set operation", {
|
|
422
|
+
key
|
|
423
|
+
});
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
const serializedValue = JSON.stringify(value);
|
|
428
|
+
await this.client.setex(key, expirationSeconds, serializedValue);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
_Cache.logger.warn(
|
|
431
|
+
"Failed to set cache value, continuing without caching",
|
|
432
|
+
{
|
|
433
|
+
key,
|
|
434
|
+
error: error instanceof Error ? error.message : String(error)
|
|
435
|
+
}
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Get a value from the cache
|
|
441
|
+
* @param key - The cache key
|
|
442
|
+
* @returns The cached value or null if not found, expired, or error occurred
|
|
443
|
+
*/
|
|
444
|
+
async get(key) {
|
|
445
|
+
if (!this.initialized || !this.client) {
|
|
446
|
+
_Cache.logger.warn(
|
|
447
|
+
"Cache not initialized, returning null for get operation",
|
|
448
|
+
{ key }
|
|
449
|
+
);
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
const serializedValue = await this.client.get(key);
|
|
454
|
+
if (serializedValue === null) {
|
|
455
|
+
return null;
|
|
456
|
+
}
|
|
457
|
+
const value = JSON.parse(serializedValue);
|
|
458
|
+
return value;
|
|
459
|
+
} catch (error) {
|
|
460
|
+
_Cache.logger.warn("Failed to get cache value, returning null", {
|
|
461
|
+
key,
|
|
462
|
+
error: error instanceof Error ? error.message : String(error)
|
|
463
|
+
});
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Invalidate (delete) a cache entry
|
|
469
|
+
* @param key - The cache key to invalidate
|
|
470
|
+
*/
|
|
471
|
+
async invalidate(key) {
|
|
472
|
+
if (!this.initialized || !this.client) {
|
|
473
|
+
_Cache.logger.warn(
|
|
474
|
+
"Cache not initialized, skipping invalidate operation",
|
|
475
|
+
{ key }
|
|
476
|
+
);
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
await this.client.del(key);
|
|
481
|
+
} catch (error) {
|
|
482
|
+
_Cache.logger.warn(
|
|
483
|
+
"Failed to invalidate cache key, continuing without invalidation",
|
|
484
|
+
{
|
|
485
|
+
key,
|
|
486
|
+
error: error instanceof Error ? error.message : String(error)
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Disconnect the Redis client (for cleanup during shutdown)
|
|
493
|
+
*/
|
|
494
|
+
async disconnect() {
|
|
495
|
+
_Cache.logger.info("Disconnecting cache client");
|
|
496
|
+
if (this.client) {
|
|
497
|
+
try {
|
|
498
|
+
await this.client.disconnect();
|
|
499
|
+
_Cache.logger.info("Cache client disconnected successfully");
|
|
500
|
+
} catch (error) {
|
|
501
|
+
_Cache.logger.error("Error disconnecting cache client", {
|
|
502
|
+
error: error instanceof Error ? error.message : String(error)
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
this.initialized = false;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Get cache statistics (for monitoring/debugging)
|
|
510
|
+
*/
|
|
511
|
+
async getStats() {
|
|
512
|
+
return {
|
|
513
|
+
connected: this.client?.status === "ready",
|
|
514
|
+
initialized: this.initialized
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
// packages/shared/src/types/brandedTypes.ts
|
|
520
|
+
function brandedIdFactory() {
|
|
521
|
+
return (id) => id;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// packages/shared/src/repositories/AbstractRepository.ts
|
|
525
|
+
var import_common = require("@nestjs/common");
|
|
526
|
+
|
|
527
|
+
// packages/shared/src/sse/RedisSSEClient.ts
|
|
528
|
+
var import_ioredis2 = __toESM(require("ioredis"));
|
|
529
|
+
var origin4 = "RedisSSEClient";
|
|
530
|
+
var RedisSSEClient = class _RedisSSEClient {
|
|
531
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
532
|
+
constructor() {
|
|
533
|
+
this.initialized = false;
|
|
534
|
+
}
|
|
535
|
+
static {
|
|
536
|
+
this.logger = new PackmindLogger(origin4);
|
|
537
|
+
}
|
|
538
|
+
static getInstance() {
|
|
539
|
+
_RedisSSEClient.logger.debug("Getting RedisSSEClient instance");
|
|
540
|
+
if (!_RedisSSEClient.instance) {
|
|
541
|
+
_RedisSSEClient.logger.info("Creating new RedisSSEClient instance");
|
|
542
|
+
_RedisSSEClient.instance = new _RedisSSEClient();
|
|
543
|
+
}
|
|
544
|
+
return _RedisSSEClient.instance;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Initialize Redis clients using the same configuration as BullMQ
|
|
548
|
+
*/
|
|
549
|
+
async initialize() {
|
|
550
|
+
if (this.initialized) return;
|
|
551
|
+
_RedisSSEClient.logger.info("Initializing Redis SSE clients");
|
|
552
|
+
try {
|
|
553
|
+
const redisURI = await Configuration.getConfig("REDIS_URI") || "redis";
|
|
554
|
+
this.publisherClient = new import_ioredis2.default(redisURI);
|
|
555
|
+
this.subscriberClient = new import_ioredis2.default(redisURI);
|
|
556
|
+
this.publisherClient.on("error", (error) => {
|
|
557
|
+
_RedisSSEClient.logger.error("Redis publisher client error", {
|
|
558
|
+
error: error.message
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
this.subscriberClient.on("error", (error) => {
|
|
562
|
+
_RedisSSEClient.logger.error("Redis subscriber client error", {
|
|
563
|
+
error: error.message
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
await this.publisherClient.ping();
|
|
567
|
+
await this.subscriberClient.ping();
|
|
568
|
+
this.initialized = true;
|
|
569
|
+
_RedisSSEClient.logger.info("Redis SSE clients initialized successfully");
|
|
570
|
+
} catch (error) {
|
|
571
|
+
_RedisSSEClient.logger.error("Failed to initialize Redis SSE clients", {
|
|
572
|
+
error: error instanceof Error ? error.message : String(error)
|
|
573
|
+
});
|
|
574
|
+
throw error;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
/**
|
|
578
|
+
* Publish a message to a Redis channel
|
|
579
|
+
*/
|
|
580
|
+
async publish(channel, message) {
|
|
581
|
+
await this.initialize();
|
|
582
|
+
if (!this.publisherClient) {
|
|
583
|
+
throw new Error("Publisher client not initialized");
|
|
584
|
+
}
|
|
585
|
+
_RedisSSEClient.logger.debug("Publishing message to Redis channel", {
|
|
586
|
+
channel,
|
|
587
|
+
messageLength: message.length
|
|
588
|
+
});
|
|
589
|
+
try {
|
|
590
|
+
const subscriberCount = await this.publisherClient.publish(
|
|
591
|
+
channel,
|
|
592
|
+
message
|
|
593
|
+
);
|
|
594
|
+
_RedisSSEClient.logger.debug("Message published successfully", {
|
|
595
|
+
channel,
|
|
596
|
+
subscriberCount
|
|
597
|
+
});
|
|
598
|
+
return subscriberCount;
|
|
599
|
+
} catch (error) {
|
|
600
|
+
_RedisSSEClient.logger.error("Failed to publish message", {
|
|
601
|
+
channel,
|
|
602
|
+
error: error instanceof Error ? error.message : String(error)
|
|
603
|
+
});
|
|
604
|
+
throw error;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Subscribe to a Redis channel
|
|
609
|
+
*/
|
|
610
|
+
async subscribe(channel, callback) {
|
|
611
|
+
await this.initialize();
|
|
612
|
+
if (!this.subscriberClient) {
|
|
613
|
+
throw new Error("Subscriber client not initialized");
|
|
614
|
+
}
|
|
615
|
+
_RedisSSEClient.logger.info("Subscribing to Redis channel", { channel });
|
|
616
|
+
try {
|
|
617
|
+
this.subscriberClient.on("message", (receivedChannel, message) => {
|
|
618
|
+
if (receivedChannel === channel) {
|
|
619
|
+
_RedisSSEClient.logger.debug("Received message from Redis channel", {
|
|
620
|
+
channel: receivedChannel,
|
|
621
|
+
messageLength: message.length
|
|
622
|
+
});
|
|
623
|
+
callback(message);
|
|
624
|
+
}
|
|
625
|
+
});
|
|
626
|
+
await this.subscriberClient.subscribe(channel);
|
|
627
|
+
_RedisSSEClient.logger.info("Successfully subscribed to Redis channel", {
|
|
628
|
+
channel
|
|
629
|
+
});
|
|
630
|
+
} catch (error) {
|
|
631
|
+
_RedisSSEClient.logger.error("Failed to subscribe to Redis channel", {
|
|
632
|
+
channel,
|
|
633
|
+
error: error instanceof Error ? error.message : String(error)
|
|
634
|
+
});
|
|
635
|
+
throw error;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* Unsubscribe from a Redis channel
|
|
640
|
+
*/
|
|
641
|
+
async unsubscribe(channel) {
|
|
642
|
+
if (!this.subscriberClient) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
_RedisSSEClient.logger.info("Unsubscribing from Redis channel", { channel });
|
|
646
|
+
try {
|
|
647
|
+
await this.subscriberClient.unsubscribe(channel);
|
|
648
|
+
_RedisSSEClient.logger.info(
|
|
649
|
+
"Successfully unsubscribed from Redis channel",
|
|
650
|
+
{ channel }
|
|
651
|
+
);
|
|
652
|
+
} catch (error) {
|
|
653
|
+
_RedisSSEClient.logger.error("Failed to unsubscribe from Redis channel", {
|
|
654
|
+
channel,
|
|
655
|
+
error: error instanceof Error ? error.message : String(error)
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Clean up Redis connections
|
|
661
|
+
*/
|
|
662
|
+
async disconnect() {
|
|
663
|
+
_RedisSSEClient.logger.info("Disconnecting Redis SSE clients");
|
|
664
|
+
try {
|
|
665
|
+
if (this.publisherClient) {
|
|
666
|
+
this.publisherClient.disconnect();
|
|
667
|
+
}
|
|
668
|
+
if (this.subscriberClient) {
|
|
669
|
+
this.subscriberClient.disconnect();
|
|
670
|
+
}
|
|
671
|
+
this.initialized = false;
|
|
672
|
+
_RedisSSEClient.logger.info("Redis SSE clients disconnected successfully");
|
|
673
|
+
} catch (error) {
|
|
674
|
+
_RedisSSEClient.logger.error("Error disconnecting Redis SSE clients", {
|
|
675
|
+
error: error instanceof Error ? error.message : String(error)
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Get connection status
|
|
681
|
+
*/
|
|
682
|
+
isConnected() {
|
|
683
|
+
return this.initialized && this.publisherClient?.status === "ready" && this.subscriberClient?.status === "ready";
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
|
|
687
|
+
// packages/shared/src/sse/types.ts
|
|
688
|
+
var SSE_REDIS_CHANNELS = {
|
|
689
|
+
/**
|
|
690
|
+
* Channel for subscription management messages
|
|
691
|
+
* Used when clients subscribe/unsubscribe to specific event types
|
|
692
|
+
*/
|
|
693
|
+
SUBSCRIPTIONS: "sse:subscriptions",
|
|
694
|
+
/**
|
|
695
|
+
* Channel for event notifications
|
|
696
|
+
* Used to broadcast SSE events to all API instances
|
|
697
|
+
*/
|
|
698
|
+
EVENTS: "sse:events"
|
|
699
|
+
};
|
|
700
|
+
function createSSEEventMessage(eventType, params, data, targetUserIds) {
|
|
701
|
+
return {
|
|
702
|
+
eventType,
|
|
703
|
+
params,
|
|
704
|
+
data,
|
|
705
|
+
targetUserIds,
|
|
706
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function serializeSSERedisMessage(message) {
|
|
710
|
+
try {
|
|
711
|
+
return JSON.stringify(message);
|
|
712
|
+
} catch (error) {
|
|
713
|
+
throw new Error(
|
|
714
|
+
`Failed to serialize SSE Redis message: ${error instanceof Error ? error.message : String(error)}`
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
// packages/shared/src/types/sse/SSEEvent.ts
|
|
720
|
+
function createProgramStatusChangeEvent(ruleId, language) {
|
|
721
|
+
return {
|
|
722
|
+
type: "PROGRAM_STATUS_CHANGE",
|
|
723
|
+
data: { ruleId, language },
|
|
724
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
725
|
+
};
|
|
726
|
+
}
|
|
727
|
+
function createAssessmentStatusChangeEvent(ruleId, language) {
|
|
728
|
+
return {
|
|
729
|
+
type: "ASSESSMENT_STATUS_CHANGE",
|
|
730
|
+
data: {
|
|
731
|
+
ruleId,
|
|
732
|
+
language
|
|
733
|
+
},
|
|
734
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
function createUserContextChangeEvent(userId, organizationId, changeType, role) {
|
|
738
|
+
return {
|
|
739
|
+
type: "USER_CONTEXT_CHANGE",
|
|
740
|
+
data: {
|
|
741
|
+
userId,
|
|
742
|
+
organizationId,
|
|
743
|
+
changeType,
|
|
744
|
+
role
|
|
745
|
+
},
|
|
746
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// packages/shared/src/sse/SSEEventPublisher.ts
|
|
751
|
+
var origin5 = "SSEEventPublisher";
|
|
752
|
+
var SSEEventPublisher = class _SSEEventPublisher {
|
|
753
|
+
static {
|
|
754
|
+
this.logger = new PackmindLogger(origin5);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Get the singleton instance
|
|
758
|
+
*/
|
|
759
|
+
static getInstance() {
|
|
760
|
+
_SSEEventPublisher.logger.debug("Getting SSEEventPublisher instance");
|
|
761
|
+
if (!_SSEEventPublisher.instance) {
|
|
762
|
+
_SSEEventPublisher.logger.info("Creating new SSEEventPublisher instance");
|
|
763
|
+
_SSEEventPublisher.instance = new _SSEEventPublisher();
|
|
764
|
+
_SSEEventPublisher.redisClient = RedisSSEClient.getInstance();
|
|
765
|
+
}
|
|
766
|
+
return _SSEEventPublisher.instance;
|
|
767
|
+
}
|
|
768
|
+
constructor() {
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* Publish a program status change event for cache invalidation
|
|
772
|
+
* This triggers React Query to refetch the program data
|
|
773
|
+
*/
|
|
774
|
+
static async publishProgramStatusEvent(programId, ruleId, language, userId, organizationId) {
|
|
775
|
+
_SSEEventPublisher.logger.info("Publishing program status change event", {
|
|
776
|
+
programId,
|
|
777
|
+
ruleId,
|
|
778
|
+
language,
|
|
779
|
+
userId,
|
|
780
|
+
organizationId
|
|
781
|
+
});
|
|
782
|
+
try {
|
|
783
|
+
const event = createProgramStatusChangeEvent(ruleId, language);
|
|
784
|
+
await _SSEEventPublisher.publishEvent(
|
|
785
|
+
"program_status_change",
|
|
786
|
+
[ruleId, language],
|
|
787
|
+
event,
|
|
788
|
+
userId ? [userId] : void 0
|
|
789
|
+
);
|
|
790
|
+
_SSEEventPublisher.logger.debug(
|
|
791
|
+
"Successfully published program status change event",
|
|
792
|
+
{ programId, ruleId, language }
|
|
793
|
+
);
|
|
794
|
+
} catch (error) {
|
|
795
|
+
_SSEEventPublisher.logger.error(
|
|
796
|
+
"Failed to publish program status change event",
|
|
797
|
+
{
|
|
798
|
+
programId,
|
|
799
|
+
userId,
|
|
800
|
+
error: error instanceof Error ? error.message : String(error)
|
|
801
|
+
}
|
|
802
|
+
);
|
|
803
|
+
throw error;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Publish an assessment status change event for cache invalidation
|
|
808
|
+
* This triggers React Query to refetch the assessment data
|
|
809
|
+
*/
|
|
810
|
+
static async publishAssessmentStatusEvent(ruleId, language, userId, organizationId) {
|
|
811
|
+
_SSEEventPublisher.logger.info("Publishing assessment status change event", {
|
|
812
|
+
ruleId,
|
|
813
|
+
language,
|
|
814
|
+
userId,
|
|
815
|
+
organizationId
|
|
816
|
+
});
|
|
817
|
+
try {
|
|
818
|
+
const event = createAssessmentStatusChangeEvent(ruleId, language);
|
|
819
|
+
await _SSEEventPublisher.publishEvent(
|
|
820
|
+
"assessment_status_change",
|
|
821
|
+
[ruleId, language],
|
|
822
|
+
event,
|
|
823
|
+
userId ? [userId] : void 0
|
|
824
|
+
);
|
|
825
|
+
_SSEEventPublisher.logger.debug(
|
|
826
|
+
"Successfully published assessment status change event",
|
|
827
|
+
{ ruleId, language }
|
|
828
|
+
);
|
|
829
|
+
} catch (error) {
|
|
830
|
+
_SSEEventPublisher.logger.error(
|
|
831
|
+
"Failed to publish assessment status change event",
|
|
832
|
+
{
|
|
833
|
+
userId,
|
|
834
|
+
error: error instanceof Error ? error.message : String(error)
|
|
835
|
+
}
|
|
836
|
+
);
|
|
837
|
+
throw error;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
/**
|
|
841
|
+
* Publish an event to notify a user that their context (role or membership) changed
|
|
842
|
+
* This should trigger a refetch of the /me route on the frontend
|
|
843
|
+
*/
|
|
844
|
+
static async publishUserContextChangeEvent(userId, organizationId, changeType, role) {
|
|
845
|
+
_SSEEventPublisher.logger.info("Publishing user context change event", {
|
|
846
|
+
userId,
|
|
847
|
+
organizationId,
|
|
848
|
+
changeType,
|
|
849
|
+
role
|
|
850
|
+
});
|
|
851
|
+
try {
|
|
852
|
+
const event = createUserContextChangeEvent(
|
|
853
|
+
userId,
|
|
854
|
+
organizationId,
|
|
855
|
+
changeType,
|
|
856
|
+
role
|
|
857
|
+
);
|
|
858
|
+
await _SSEEventPublisher.publishEvent("user_context_change", [], event, [
|
|
859
|
+
userId
|
|
860
|
+
]);
|
|
861
|
+
_SSEEventPublisher.logger.debug(
|
|
862
|
+
"Successfully published user context change event",
|
|
863
|
+
{
|
|
864
|
+
userId,
|
|
865
|
+
organizationId,
|
|
866
|
+
changeType
|
|
867
|
+
}
|
|
868
|
+
);
|
|
869
|
+
} catch (error) {
|
|
870
|
+
_SSEEventPublisher.logger.error(
|
|
871
|
+
"Failed to publish user context change event",
|
|
872
|
+
{
|
|
873
|
+
userId,
|
|
874
|
+
organizationId,
|
|
875
|
+
changeType,
|
|
876
|
+
error: error instanceof Error ? error.message : String(error)
|
|
877
|
+
}
|
|
878
|
+
);
|
|
879
|
+
throw error;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Generic method to publish any SSE event type to Redis pub/sub
|
|
884
|
+
*/
|
|
885
|
+
static async publishEvent(eventType, params, data, targetUserIds) {
|
|
886
|
+
_SSEEventPublisher.logger.info("Publishing SSE event", {
|
|
887
|
+
eventType,
|
|
888
|
+
params,
|
|
889
|
+
targetUserIds: targetUserIds?.length || "all"
|
|
890
|
+
});
|
|
891
|
+
try {
|
|
892
|
+
_SSEEventPublisher.getInstance();
|
|
893
|
+
const redisClient = _SSEEventPublisher.redisClient;
|
|
894
|
+
const message = createSSEEventMessage(
|
|
895
|
+
eventType,
|
|
896
|
+
params,
|
|
897
|
+
data,
|
|
898
|
+
targetUserIds
|
|
899
|
+
);
|
|
900
|
+
const serializedMessage = serializeSSERedisMessage(message);
|
|
901
|
+
await redisClient.publish(SSE_REDIS_CHANNELS.EVENTS, serializedMessage);
|
|
902
|
+
_SSEEventPublisher.logger.debug("Successfully published SSE event", {
|
|
903
|
+
eventType,
|
|
904
|
+
params
|
|
905
|
+
});
|
|
906
|
+
} catch (error) {
|
|
907
|
+
_SSEEventPublisher.logger.error("Failed to publish SSE event", {
|
|
908
|
+
eventType,
|
|
909
|
+
params,
|
|
910
|
+
error: error instanceof Error ? error.message : String(error)
|
|
911
|
+
});
|
|
912
|
+
throw error;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
// packages/shared/src/types/git/GitCommit.ts
|
|
918
|
+
var createGitCommitId = brandedIdFactory();
|
|
919
|
+
|
|
920
|
+
// packages/shared/src/types/git/GitProvider.ts
|
|
921
|
+
var createGitProviderId = brandedIdFactory();
|
|
922
|
+
|
|
923
|
+
// packages/shared/src/types/git/GitRepo.ts
|
|
924
|
+
var createGitRepoId = brandedIdFactory();
|
|
925
|
+
|
|
926
|
+
// packages/shared/src/types/standards/StandardVersion.ts
|
|
927
|
+
var createStandardVersionId = brandedIdFactory();
|
|
928
|
+
|
|
929
|
+
// packages/shared/src/types/standards/Rule.ts
|
|
930
|
+
var createRuleId = brandedIdFactory();
|
|
931
|
+
|
|
932
|
+
// packages/shared/src/types/standards/RuleExample.ts
|
|
933
|
+
var createRuleExampleId = brandedIdFactory();
|
|
934
|
+
|
|
935
|
+
// packages/shared/src/types/standards/Standard.ts
|
|
936
|
+
var createStandardId = brandedIdFactory();
|
|
937
|
+
|
|
938
|
+
// packages/shared/src/types/accounts/Organization.ts
|
|
939
|
+
var createOrganizationId = brandedIdFactory();
|
|
940
|
+
|
|
941
|
+
// packages/shared/src/types/accounts/User.ts
|
|
942
|
+
var createUserId = brandedIdFactory();
|
|
943
|
+
|
|
944
|
+
// packages/shared/src/types/deployments/RenderMode.ts
|
|
945
|
+
var REQUIRED_RENDER_MODE = "PACKMIND" /* PACKMIND */;
|
|
946
|
+
var RENDER_MODE_ORDER = [
|
|
947
|
+
"PACKMIND" /* PACKMIND */,
|
|
948
|
+
"AGENTS_MD" /* AGENTS_MD */,
|
|
949
|
+
"JUNIE" /* JUNIE */,
|
|
950
|
+
"GH_COPILOT" /* GH_COPILOT */,
|
|
951
|
+
"CLAUDE" /* CLAUDE */,
|
|
952
|
+
"CURSOR" /* CURSOR */
|
|
953
|
+
];
|
|
954
|
+
var normalizeRenderModes = (modes) => {
|
|
955
|
+
const uniqueModes = new Set(modes);
|
|
956
|
+
uniqueModes.add(REQUIRED_RENDER_MODE);
|
|
957
|
+
return RENDER_MODE_ORDER.filter((mode) => uniqueModes.has(mode));
|
|
958
|
+
};
|
|
959
|
+
|
|
960
|
+
// packages/shared/src/types/deployments/RenderModeConfiguration.ts
|
|
961
|
+
var createRenderModeConfigurationId = brandedIdFactory();
|
|
962
|
+
var DEFAULT_ACTIVE_RENDER_MODES = normalizeRenderModes([
|
|
963
|
+
"AGENTS_MD" /* AGENTS_MD */
|
|
964
|
+
]);
|
|
965
|
+
|
|
966
|
+
// packages/shared/src/types/deployments/RecipesDeployment.ts
|
|
967
|
+
var createRecipesDeploymentId = brandedIdFactory();
|
|
968
|
+
|
|
969
|
+
// packages/shared/src/types/deployments/StandardsDeployment.ts
|
|
970
|
+
var createStandardsDeploymentId = brandedIdFactory();
|
|
971
|
+
|
|
972
|
+
// packages/shared/src/types/deployments/Target.ts
|
|
973
|
+
var createTargetId = brandedIdFactory();
|
|
974
|
+
|
|
975
|
+
// packages/shared/src/types/recipes/RecipeVersion.ts
|
|
976
|
+
var createRecipeVersionId = brandedIdFactory();
|
|
977
|
+
|
|
978
|
+
// packages/shared/src/types/recipes/Recipe.ts
|
|
979
|
+
var createRecipeId = brandedIdFactory();
|
|
980
|
+
|
|
981
|
+
// packages/shared/src/types/spaces/Space.ts
|
|
982
|
+
var createSpaceId = brandedIdFactory();
|
|
983
|
+
|
|
984
|
+
// packages/shared/src/types/languages/Language.ts
|
|
985
|
+
var ProgrammingLanguage = /* @__PURE__ */ ((ProgrammingLanguage3) => {
|
|
986
|
+
ProgrammingLanguage3["JAVASCRIPT"] = "JAVASCRIPT";
|
|
987
|
+
ProgrammingLanguage3["JAVASCRIPT_JSX"] = "JAVASCRIPT_JSX";
|
|
988
|
+
ProgrammingLanguage3["TYPESCRIPT"] = "TYPESCRIPT";
|
|
989
|
+
ProgrammingLanguage3["TYPESCRIPT_TSX"] = "TYPESCRIPT_TSX";
|
|
990
|
+
ProgrammingLanguage3["PYTHON"] = "PYTHON";
|
|
991
|
+
ProgrammingLanguage3["PHP"] = "PHP";
|
|
992
|
+
ProgrammingLanguage3["JAVA"] = "JAVA";
|
|
993
|
+
ProgrammingLanguage3["SCSS"] = "SCSS";
|
|
994
|
+
ProgrammingLanguage3["HTML"] = "HTML";
|
|
995
|
+
ProgrammingLanguage3["CSHARP"] = "CSHARP";
|
|
996
|
+
ProgrammingLanguage3["GENERIC"] = "GENERIC";
|
|
997
|
+
ProgrammingLanguage3["GO"] = "GO";
|
|
998
|
+
ProgrammingLanguage3["C"] = "C";
|
|
999
|
+
ProgrammingLanguage3["CPP"] = "CPP";
|
|
1000
|
+
ProgrammingLanguage3["SQL"] = "SQL";
|
|
1001
|
+
ProgrammingLanguage3["KOTLIN"] = "KOTLIN";
|
|
1002
|
+
ProgrammingLanguage3["VUE"] = "VUE";
|
|
1003
|
+
ProgrammingLanguage3["CSS"] = "CSS";
|
|
1004
|
+
ProgrammingLanguage3["YAML"] = "YAML";
|
|
1005
|
+
ProgrammingLanguage3["JSON"] = "JSON";
|
|
1006
|
+
ProgrammingLanguage3["XML"] = "XML";
|
|
1007
|
+
ProgrammingLanguage3["BASH"] = "BASH";
|
|
1008
|
+
ProgrammingLanguage3["MARKDOWN"] = "MARKDOWN";
|
|
1009
|
+
ProgrammingLanguage3["RUBY"] = "RUBY";
|
|
1010
|
+
ProgrammingLanguage3["RUST"] = "RUST";
|
|
1011
|
+
ProgrammingLanguage3["SAP_ABAP"] = "SAP_ABAP";
|
|
1012
|
+
ProgrammingLanguage3["SAP_CDS"] = "SAP_CDS";
|
|
1013
|
+
ProgrammingLanguage3["SAP_HANA_SQL"] = "SAP_HANA_SQL";
|
|
1014
|
+
ProgrammingLanguage3["SWIFT"] = "SWIFT";
|
|
1015
|
+
ProgrammingLanguage3["PROPERTIES"] = "PROPERTIES";
|
|
1016
|
+
return ProgrammingLanguage3;
|
|
1017
|
+
})(ProgrammingLanguage || {});
|
|
1018
|
+
var ProgrammingLanguageDetails = {
|
|
1019
|
+
["GENERIC" /* GENERIC */]: {
|
|
1020
|
+
displayName: "Generic",
|
|
1021
|
+
fileExtensions: []
|
|
1022
|
+
},
|
|
1023
|
+
["JAVASCRIPT" /* JAVASCRIPT */]: {
|
|
1024
|
+
displayName: "JavaScript",
|
|
1025
|
+
fileExtensions: ["js"]
|
|
1026
|
+
},
|
|
1027
|
+
["JAVASCRIPT_JSX" /* JAVASCRIPT_JSX */]: {
|
|
1028
|
+
displayName: "JavaScript (JSX)",
|
|
1029
|
+
fileExtensions: ["jsx"]
|
|
1030
|
+
},
|
|
1031
|
+
["TYPESCRIPT" /* TYPESCRIPT */]: {
|
|
1032
|
+
displayName: "TypeScript",
|
|
1033
|
+
fileExtensions: ["ts"]
|
|
1034
|
+
},
|
|
1035
|
+
["TYPESCRIPT_TSX" /* TYPESCRIPT_TSX */]: {
|
|
1036
|
+
displayName: "TypeScript (TSX)",
|
|
1037
|
+
fileExtensions: ["tsx"]
|
|
1038
|
+
},
|
|
1039
|
+
["PYTHON" /* PYTHON */]: {
|
|
1040
|
+
displayName: "Python",
|
|
1041
|
+
fileExtensions: ["py", "pyx", "pyw"]
|
|
1042
|
+
},
|
|
1043
|
+
["PHP" /* PHP */]: {
|
|
1044
|
+
displayName: "PHP",
|
|
1045
|
+
fileExtensions: ["php", "phtml"]
|
|
1046
|
+
},
|
|
1047
|
+
["JAVA" /* JAVA */]: {
|
|
1048
|
+
displayName: "Java",
|
|
1049
|
+
fileExtensions: ["java"]
|
|
1050
|
+
},
|
|
1051
|
+
["SCSS" /* SCSS */]: {
|
|
1052
|
+
displayName: "SCSS",
|
|
1053
|
+
fileExtensions: ["scss"]
|
|
1054
|
+
},
|
|
1055
|
+
["HTML" /* HTML */]: {
|
|
1056
|
+
displayName: "HTML",
|
|
1057
|
+
fileExtensions: ["html", "htm"]
|
|
1058
|
+
},
|
|
1059
|
+
["CSHARP" /* CSHARP */]: {
|
|
1060
|
+
displayName: "C#",
|
|
1061
|
+
fileExtensions: ["cs"]
|
|
1062
|
+
},
|
|
1063
|
+
["GO" /* GO */]: {
|
|
1064
|
+
displayName: "Go",
|
|
1065
|
+
fileExtensions: ["go"]
|
|
1066
|
+
},
|
|
1067
|
+
["C" /* C */]: {
|
|
1068
|
+
displayName: "C",
|
|
1069
|
+
fileExtensions: ["c", "h"]
|
|
1070
|
+
},
|
|
1071
|
+
["CPP" /* CPP */]: {
|
|
1072
|
+
displayName: "C++",
|
|
1073
|
+
fileExtensions: ["cpp", "cc", "cxx", "c++", "hpp", "hxx"]
|
|
1074
|
+
},
|
|
1075
|
+
["SQL" /* SQL */]: {
|
|
1076
|
+
displayName: "SQL",
|
|
1077
|
+
fileExtensions: ["sql"]
|
|
1078
|
+
},
|
|
1079
|
+
["KOTLIN" /* KOTLIN */]: {
|
|
1080
|
+
displayName: "Kotlin",
|
|
1081
|
+
fileExtensions: ["kt", "kts"]
|
|
1082
|
+
},
|
|
1083
|
+
["VUE" /* VUE */]: {
|
|
1084
|
+
displayName: "Vue",
|
|
1085
|
+
fileExtensions: ["vue"]
|
|
1086
|
+
},
|
|
1087
|
+
["CSS" /* CSS */]: {
|
|
1088
|
+
displayName: "CSS",
|
|
1089
|
+
fileExtensions: ["css"]
|
|
1090
|
+
},
|
|
1091
|
+
["YAML" /* YAML */]: {
|
|
1092
|
+
displayName: "YAML",
|
|
1093
|
+
fileExtensions: ["yaml", "yml"]
|
|
1094
|
+
},
|
|
1095
|
+
["JSON" /* JSON */]: {
|
|
1096
|
+
displayName: "JSON",
|
|
1097
|
+
fileExtensions: ["json"]
|
|
1098
|
+
},
|
|
1099
|
+
["XML" /* XML */]: {
|
|
1100
|
+
displayName: "XML",
|
|
1101
|
+
fileExtensions: ["xml"]
|
|
1102
|
+
},
|
|
1103
|
+
["BASH" /* BASH */]: {
|
|
1104
|
+
displayName: "Bash",
|
|
1105
|
+
fileExtensions: ["sh", "bash"]
|
|
1106
|
+
},
|
|
1107
|
+
["MARKDOWN" /* MARKDOWN */]: {
|
|
1108
|
+
displayName: "Markdown",
|
|
1109
|
+
fileExtensions: ["md"]
|
|
1110
|
+
},
|
|
1111
|
+
["RUST" /* RUST */]: {
|
|
1112
|
+
displayName: "Rust",
|
|
1113
|
+
fileExtensions: ["rs"]
|
|
1114
|
+
},
|
|
1115
|
+
["RUBY" /* RUBY */]: {
|
|
1116
|
+
displayName: "Ruby",
|
|
1117
|
+
fileExtensions: ["rb"]
|
|
1118
|
+
},
|
|
1119
|
+
["SAP_ABAP" /* SAP_ABAP */]: {
|
|
1120
|
+
displayName: "SAP ABAP",
|
|
1121
|
+
fileExtensions: ["abap", "ab4"]
|
|
1122
|
+
},
|
|
1123
|
+
["SAP_CDS" /* SAP_CDS */]: {
|
|
1124
|
+
displayName: "SAP CDS",
|
|
1125
|
+
fileExtensions: ["cds"]
|
|
1126
|
+
},
|
|
1127
|
+
["SAP_HANA_SQL" /* SAP_HANA_SQL */]: {
|
|
1128
|
+
displayName: "SAP HANA SQL",
|
|
1129
|
+
fileExtensions: [
|
|
1130
|
+
"hdbprocedure",
|
|
1131
|
+
"hdbfunction",
|
|
1132
|
+
"hdbview",
|
|
1133
|
+
"hdbcalculationview"
|
|
1134
|
+
]
|
|
1135
|
+
},
|
|
1136
|
+
["SWIFT" /* SWIFT */]: {
|
|
1137
|
+
displayName: "Swift",
|
|
1138
|
+
fileExtensions: ["swift"]
|
|
1139
|
+
},
|
|
1140
|
+
["PROPERTIES" /* PROPERTIES */]: {
|
|
1141
|
+
displayName: "Properties",
|
|
1142
|
+
fileExtensions: ["properties"]
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
var stringToProgrammingLanguage = (input) => {
|
|
1146
|
+
const trimmedInput = input.trim();
|
|
1147
|
+
if (!trimmedInput) {
|
|
1148
|
+
throw new Error("Language input cannot be empty");
|
|
1149
|
+
}
|
|
1150
|
+
const lowerInput = trimmedInput.toLowerCase();
|
|
1151
|
+
for (const enumValue of Object.values(ProgrammingLanguage)) {
|
|
1152
|
+
if (enumValue.toLowerCase() === lowerInput) {
|
|
1153
|
+
return enumValue;
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
for (const [language, info] of Object.entries(ProgrammingLanguageDetails)) {
|
|
1157
|
+
if (info.displayName.toLowerCase() === lowerInput) {
|
|
1158
|
+
return language;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
for (const [language, info] of Object.entries(ProgrammingLanguageDetails)) {
|
|
1162
|
+
if (info.fileExtensions.some((ext) => ext.toLowerCase() === lowerInput)) {
|
|
1163
|
+
return language;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const availableLanguages = Object.values(ProgrammingLanguageDetails).map((info) => info.displayName).join(", ");
|
|
1167
|
+
throw new Error(
|
|
1168
|
+
`Unknown programming language: "${trimmedInput}". Available languages: ${availableLanguages}`
|
|
1169
|
+
);
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
// packages/shared/src/mail/SmtpMailService.ts
|
|
1173
|
+
var import_nodemailer = __toESM(require("nodemailer"));
|
|
1174
|
+
|
|
1175
|
+
// packages/shared/src/dataSources/local.ts
|
|
1176
|
+
var import_typeorm = require("typeorm");
|
|
1177
|
+
var dataSource = makeDatasource();
|
|
1178
|
+
function makeDatasource() {
|
|
1179
|
+
try {
|
|
1180
|
+
return new import_typeorm.DataSource({
|
|
1181
|
+
type: "postgres",
|
|
1182
|
+
url: process.env["DATABASE_URL"],
|
|
1183
|
+
entities: [],
|
|
1184
|
+
migrations: []
|
|
1185
|
+
});
|
|
1186
|
+
} catch {
|
|
1187
|
+
return {};
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// apps/cli/src/application/useCases/ExecuteSingleFileAstUseCase.ts
|
|
1192
|
+
var ExecuteSingleFileAstUseCase = class _ExecuteSingleFileAstUseCase {
|
|
1193
|
+
constructor(linterExecutionUseCase) {
|
|
1194
|
+
this.linterExecutionUseCase = linterExecutionUseCase;
|
|
1195
|
+
}
|
|
1196
|
+
static {
|
|
1197
|
+
this.fallbackStandardSlug = "adhoc-linter";
|
|
1198
|
+
}
|
|
1199
|
+
static {
|
|
1200
|
+
this.fallbackRuleContent = "adhoc-rule";
|
|
1201
|
+
}
|
|
1202
|
+
async execute(command2) {
|
|
1203
|
+
const { program, fileContent, language } = command2;
|
|
1204
|
+
const result = await this.linterExecutionUseCase.execute({
|
|
1205
|
+
filePath: "cli-single-file",
|
|
1206
|
+
fileContent,
|
|
1207
|
+
language,
|
|
1208
|
+
programs: [
|
|
1209
|
+
{
|
|
1210
|
+
code: program,
|
|
1211
|
+
ruleContent: _ExecuteSingleFileAstUseCase.fallbackRuleContent,
|
|
1212
|
+
standardSlug: _ExecuteSingleFileAstUseCase.fallbackStandardSlug,
|
|
1213
|
+
sourceCodeState: "AST",
|
|
1214
|
+
language
|
|
1215
|
+
}
|
|
1216
|
+
]
|
|
1217
|
+
});
|
|
1218
|
+
return result.violations.map(({ line, character }) => ({
|
|
1219
|
+
line,
|
|
1220
|
+
character
|
|
1221
|
+
}));
|
|
1222
|
+
}
|
|
1223
|
+
};
|
|
1224
|
+
|
|
1225
|
+
// apps/cli/src/application/services/GitService.ts
|
|
1226
|
+
var import_child_process = require("child_process");
|
|
1227
|
+
var import_util = require("util");
|
|
1228
|
+
var execAsync = (0, import_util.promisify)(import_child_process.exec);
|
|
1229
|
+
var origin6 = "GitService";
|
|
1230
|
+
var GitService = class {
|
|
1231
|
+
constructor(logger = new PackmindLogger(origin6)) {
|
|
1232
|
+
this.logger = logger;
|
|
1233
|
+
}
|
|
1234
|
+
async getGitRepositoryRoot(path2) {
|
|
1235
|
+
try {
|
|
1236
|
+
const { stdout } = await execAsync("git rev-parse --show-toplevel", {
|
|
1237
|
+
cwd: path2
|
|
1238
|
+
});
|
|
1239
|
+
const gitRoot = stdout.trim();
|
|
1240
|
+
this.logger.debug("Resolved git repository root", {
|
|
1241
|
+
inputPath: path2,
|
|
1242
|
+
gitRoot
|
|
1243
|
+
});
|
|
1244
|
+
return gitRoot;
|
|
1245
|
+
} catch (error) {
|
|
1246
|
+
if (error instanceof Error) {
|
|
1247
|
+
throw new Error(
|
|
1248
|
+
`Failed to get Git repository root. The path '${path2}' does not appear to be inside a Git repository.
|
|
1249
|
+
${error.message}`
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
throw new Error("Failed to get Git repository root: Unknown error");
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
async getCurrentBranches(repoPath) {
|
|
1256
|
+
try {
|
|
1257
|
+
const { stdout } = await execAsync("git branch -a --contains HEAD", {
|
|
1258
|
+
cwd: repoPath
|
|
1259
|
+
});
|
|
1260
|
+
const branches = this.parseBranches(stdout);
|
|
1261
|
+
return { branches };
|
|
1262
|
+
} catch (error) {
|
|
1263
|
+
if (error instanceof Error) {
|
|
1264
|
+
throw new Error(
|
|
1265
|
+
`Failed to get Git branches. packmind-cli lint must be run in a Git repository.
|
|
1266
|
+
${error.message}`
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
throw new Error("Failed to get Git branches: Unknown error");
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
async getGitRemoteUrl(repoPath, origin10) {
|
|
1273
|
+
try {
|
|
1274
|
+
const { stdout } = await execAsync("git remote -v", {
|
|
1275
|
+
cwd: repoPath
|
|
1276
|
+
});
|
|
1277
|
+
const remotes = this.parseRemotes(stdout);
|
|
1278
|
+
if (remotes.length === 0) {
|
|
1279
|
+
throw new Error("No Git remotes found in the repository");
|
|
1280
|
+
}
|
|
1281
|
+
let selectedRemote;
|
|
1282
|
+
if (origin10) {
|
|
1283
|
+
const foundRemote = remotes.find((remote) => remote.name === origin10);
|
|
1284
|
+
if (!foundRemote) {
|
|
1285
|
+
throw new Error(`Remote '${origin10}' not found in repository`);
|
|
1286
|
+
}
|
|
1287
|
+
selectedRemote = foundRemote.url;
|
|
1288
|
+
} else if (remotes.length === 1) {
|
|
1289
|
+
selectedRemote = remotes[0].url;
|
|
1290
|
+
} else {
|
|
1291
|
+
const originRemote = remotes.find((remote) => remote.name === "origin");
|
|
1292
|
+
if (!originRemote) {
|
|
1293
|
+
throw new Error(
|
|
1294
|
+
"Multiple remotes found but no 'origin' remote. Please specify the remote name."
|
|
1295
|
+
);
|
|
1296
|
+
}
|
|
1297
|
+
selectedRemote = originRemote.url;
|
|
1298
|
+
}
|
|
1299
|
+
return {
|
|
1300
|
+
gitRemoteUrl: this.normalizeGitUrl(selectedRemote)
|
|
1301
|
+
};
|
|
1302
|
+
} catch (error) {
|
|
1303
|
+
if (error instanceof Error) {
|
|
1304
|
+
throw new Error(
|
|
1305
|
+
`Failed to get Git remote URL. packmind-cli lint must be run in a Git repository.
|
|
1306
|
+
${error.message}`
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
throw new Error("Failed to get Git remote URL: Unknown error");
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
parseRemotes(gitRemoteOutput) {
|
|
1313
|
+
const lines = gitRemoteOutput.trim().split("\n");
|
|
1314
|
+
const remotes = [];
|
|
1315
|
+
for (const line of lines) {
|
|
1316
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+\((\w+)\)$/);
|
|
1317
|
+
if (match) {
|
|
1318
|
+
const [, name, url, type] = match;
|
|
1319
|
+
if (type === "fetch") {
|
|
1320
|
+
remotes.push({ name, url, type });
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
return remotes;
|
|
1325
|
+
}
|
|
1326
|
+
parseBranches(gitBranchOutput) {
|
|
1327
|
+
const lines = gitBranchOutput.trim().split("\n");
|
|
1328
|
+
const branchNames = /* @__PURE__ */ new Set();
|
|
1329
|
+
for (const line of lines) {
|
|
1330
|
+
let branchName = line.trim();
|
|
1331
|
+
if (branchName.startsWith("* ")) {
|
|
1332
|
+
branchName = branchName.substring(2).trim();
|
|
1333
|
+
}
|
|
1334
|
+
if (branchName.startsWith("remotes/origin/")) {
|
|
1335
|
+
branchName = branchName.substring("remotes/origin/".length);
|
|
1336
|
+
}
|
|
1337
|
+
if (branchName.startsWith("remotes/")) {
|
|
1338
|
+
const parts = branchName.split("/");
|
|
1339
|
+
if (parts.length > 2) {
|
|
1340
|
+
branchName = parts.slice(2).join("/");
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
if (branchName.includes("HEAD ->")) {
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
if (branchName) {
|
|
1347
|
+
branchNames.add(branchName);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return Array.from(branchNames);
|
|
1351
|
+
}
|
|
1352
|
+
normalizeGitUrl(url) {
|
|
1353
|
+
const sshMatch = url.match(/^git@([^:]+):(.+)$/);
|
|
1354
|
+
if (sshMatch) {
|
|
1355
|
+
const [, host, path2] = sshMatch;
|
|
1356
|
+
const cleanPath = path2.replace(/\.git$/, "");
|
|
1357
|
+
return `${host}/${cleanPath}`;
|
|
1358
|
+
}
|
|
1359
|
+
const httpsMatch = url.match(/^https?:\/\/([^/]+)\/(.+)$/);
|
|
1360
|
+
if (httpsMatch) {
|
|
1361
|
+
const [, host, path2] = httpsMatch;
|
|
1362
|
+
const cleanPath = path2.replace(/\.git$/, "");
|
|
1363
|
+
return `${host}/${cleanPath}`;
|
|
1364
|
+
}
|
|
1365
|
+
return url;
|
|
1366
|
+
}
|
|
1367
|
+
};
|
|
1368
|
+
|
|
1369
|
+
// apps/cli/src/application/useCases/GetGitRemoteUrlUseCase.ts
|
|
1370
|
+
var GetGitRemoteUrlUseCase = class {
|
|
1371
|
+
constructor(gitRemoteUrlService = new GitService()) {
|
|
1372
|
+
this.gitRemoteUrlService = gitRemoteUrlService;
|
|
1373
|
+
}
|
|
1374
|
+
async execute(command2) {
|
|
1375
|
+
const { path: repoPath, origin: origin10 } = command2;
|
|
1376
|
+
return this.gitRemoteUrlService.getGitRemoteUrl(repoPath, origin10);
|
|
1377
|
+
}
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
// apps/cli/src/application/services/ListFiles.ts
|
|
1381
|
+
var fs = __toESM(require("fs/promises"));
|
|
1382
|
+
var path = __toESM(require("path"));
|
|
1383
|
+
var ListFiles = class {
|
|
1384
|
+
async listFilesInDirectory(directoryPath, extensions, excludes = [], skipHidden = true) {
|
|
1385
|
+
const results = [];
|
|
1386
|
+
const normalizedExtensions = extensions.map(
|
|
1387
|
+
(ext) => ext.startsWith(".") ? ext : `.${ext}`
|
|
1388
|
+
);
|
|
1389
|
+
const includeAllExtensions = normalizedExtensions.length === 0;
|
|
1390
|
+
await this.findFilesRecursively(
|
|
1391
|
+
directoryPath,
|
|
1392
|
+
normalizedExtensions,
|
|
1393
|
+
excludes,
|
|
1394
|
+
results,
|
|
1395
|
+
includeAllExtensions,
|
|
1396
|
+
skipHidden
|
|
1397
|
+
);
|
|
1398
|
+
return results;
|
|
1399
|
+
}
|
|
1400
|
+
async findFilesRecursively(directoryPath, extensions, excludes, results, includeAllExtensions, skipHidden) {
|
|
1401
|
+
try {
|
|
1402
|
+
const entries = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
1403
|
+
for (const entry of entries) {
|
|
1404
|
+
const fullPath = path.join(directoryPath, entry.name);
|
|
1405
|
+
if (this.shouldExcludePath(fullPath, excludes)) {
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
if (skipHidden && entry.name.startsWith(".")) {
|
|
1409
|
+
continue;
|
|
1410
|
+
}
|
|
1411
|
+
if (entry.isDirectory()) {
|
|
1412
|
+
await this.findFilesRecursively(
|
|
1413
|
+
fullPath,
|
|
1414
|
+
extensions,
|
|
1415
|
+
excludes,
|
|
1416
|
+
results,
|
|
1417
|
+
includeAllExtensions,
|
|
1418
|
+
skipHidden
|
|
1419
|
+
);
|
|
1420
|
+
} else if (entry.isFile()) {
|
|
1421
|
+
const fileExtension = path.extname(entry.name);
|
|
1422
|
+
if (includeAllExtensions || extensions.includes(fileExtension)) {
|
|
1423
|
+
results.push({
|
|
1424
|
+
path: fullPath
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
} catch (error) {
|
|
1430
|
+
console.error(`Error reading directory ${directoryPath}:`, error);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
shouldExcludePath(filePath, excludes) {
|
|
1434
|
+
if (excludes.length === 0) {
|
|
1435
|
+
return false;
|
|
1436
|
+
}
|
|
1437
|
+
const normalizedPath = path.normalize(filePath).replace(/\\/g, "/");
|
|
1438
|
+
for (const exclude of excludes) {
|
|
1439
|
+
if (this.matchesGlobPattern(normalizedPath, exclude)) {
|
|
1440
|
+
return true;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
return false;
|
|
1444
|
+
}
|
|
1445
|
+
matchesGlobPattern(filePath, pattern) {
|
|
1446
|
+
if (!pattern.includes("*") && !pattern.includes("/")) {
|
|
1447
|
+
const pathSegments = filePath.split("/");
|
|
1448
|
+
return pathSegments.some((segment) => segment === pattern);
|
|
1449
|
+
}
|
|
1450
|
+
let regexPattern = pattern.replace(/\*\*/g, "__DOUBLE_STAR__").replace(/\*/g, "[^/]*").replace(/__DOUBLE_STAR__/g, ".*").replace(/\//g, "\\/");
|
|
1451
|
+
if (!pattern.startsWith("**/")) {
|
|
1452
|
+
regexPattern = "(^|/)" + regexPattern;
|
|
1453
|
+
}
|
|
1454
|
+
if (!pattern.endsWith("/**")) {
|
|
1455
|
+
regexPattern = regexPattern + "($|/)";
|
|
1456
|
+
}
|
|
1457
|
+
const regex = new RegExp(regexPattern);
|
|
1458
|
+
return regex.test(filePath);
|
|
1459
|
+
}
|
|
1460
|
+
async readFileContent(filePath) {
|
|
1461
|
+
try {
|
|
1462
|
+
return await fs.readFile(filePath, "utf-8");
|
|
1463
|
+
} catch (error) {
|
|
1464
|
+
console.error(`Error reading file ${filePath}:`, error);
|
|
1465
|
+
throw error;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1469
|
+
|
|
1470
|
+
// apps/cli/src/application/useCases/ListFilesInDirectoryUseCase.ts
|
|
1471
|
+
var ListFilesInDirectoryUseCase = class {
|
|
1472
|
+
constructor(listFiles = new ListFiles()) {
|
|
1473
|
+
this.listFiles = listFiles;
|
|
1474
|
+
}
|
|
1475
|
+
async execute(command2) {
|
|
1476
|
+
const { path: directoryPath, extensions, excludes = [] } = command2;
|
|
1477
|
+
const files = await this.listFiles.listFilesInDirectory(
|
|
1478
|
+
directoryPath,
|
|
1479
|
+
extensions,
|
|
1480
|
+
excludes
|
|
1481
|
+
);
|
|
1482
|
+
const filesWithContent = [];
|
|
1483
|
+
for (const file of files) {
|
|
1484
|
+
try {
|
|
1485
|
+
const content = await this.listFiles.readFileContent(file.path);
|
|
1486
|
+
filesWithContent.push({
|
|
1487
|
+
path: file.path,
|
|
1488
|
+
content
|
|
1489
|
+
});
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
console.error(`Error reading file ${file.path}:`, error);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
return filesWithContent;
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
// apps/cli/src/application/useCases/LintFilesInDirectoryUseCase.ts
|
|
1499
|
+
var import_minimatch = require("minimatch");
|
|
1500
|
+
var origin7 = "LintFilesInDirectoryUseCase";
|
|
1501
|
+
var LintFilesInDirectoryUseCase = class {
|
|
1502
|
+
constructor(services, repositories, logger = new PackmindLogger(origin7)) {
|
|
1503
|
+
this.services = services;
|
|
1504
|
+
this.repositories = repositories;
|
|
1505
|
+
this.logger = logger;
|
|
1506
|
+
}
|
|
1507
|
+
fileMatchesScope(filePath, scopePatterns) {
|
|
1508
|
+
if (!scopePatterns || scopePatterns.length === 0) {
|
|
1509
|
+
return true;
|
|
1510
|
+
}
|
|
1511
|
+
return scopePatterns.some((pattern) => (0, import_minimatch.minimatch)(filePath, pattern));
|
|
1512
|
+
}
|
|
1513
|
+
fileMatchesTargetAndScope(filePath, targetPath, scopePatterns) {
|
|
1514
|
+
if (!scopePatterns || scopePatterns.length === 0) {
|
|
1515
|
+
const effectivePattern = this.buildEffectivePattern(targetPath, null);
|
|
1516
|
+
const matches2 = (0, import_minimatch.minimatch)(filePath, effectivePattern, {
|
|
1517
|
+
matchBase: false
|
|
1518
|
+
});
|
|
1519
|
+
this.logger.debug(
|
|
1520
|
+
`File matching check: file="${filePath}", target="${targetPath}", scope=null, pattern="${effectivePattern}", matches=${matches2}`
|
|
1521
|
+
);
|
|
1522
|
+
return matches2;
|
|
1523
|
+
}
|
|
1524
|
+
const matches = scopePatterns.some((scopePattern) => {
|
|
1525
|
+
const effectivePattern = this.buildEffectivePattern(
|
|
1526
|
+
targetPath,
|
|
1527
|
+
scopePattern
|
|
1528
|
+
);
|
|
1529
|
+
const patternMatches = (0, import_minimatch.minimatch)(filePath, effectivePattern, {
|
|
1530
|
+
matchBase: false
|
|
1531
|
+
});
|
|
1532
|
+
this.logger.debug(
|
|
1533
|
+
`File matching check: file="${filePath}", target="${targetPath}", scope="${scopePattern}", pattern="${effectivePattern}", matches=${patternMatches}`
|
|
1534
|
+
);
|
|
1535
|
+
return patternMatches;
|
|
1536
|
+
});
|
|
1537
|
+
return matches;
|
|
1538
|
+
}
|
|
1539
|
+
buildEffectivePattern(targetPath, scope) {
|
|
1540
|
+
const normalizedTarget = targetPath === "/" ? "/" : targetPath.replace(/\/$/, "");
|
|
1541
|
+
if (!scope) {
|
|
1542
|
+
return normalizedTarget === "/" ? "/**" : normalizedTarget + "/**";
|
|
1543
|
+
}
|
|
1544
|
+
if (scope.startsWith(normalizedTarget + "/") || scope === normalizedTarget) {
|
|
1545
|
+
return scope.endsWith("/") ? scope + "**" : scope;
|
|
1546
|
+
}
|
|
1547
|
+
const cleanScope = scope.startsWith("/") ? scope.substring(1) : scope;
|
|
1548
|
+
let pattern;
|
|
1549
|
+
if (normalizedTarget === "/") {
|
|
1550
|
+
pattern = "/" + cleanScope;
|
|
1551
|
+
} else {
|
|
1552
|
+
pattern = normalizedTarget + "/" + cleanScope;
|
|
1553
|
+
}
|
|
1554
|
+
if (pattern.endsWith("/")) {
|
|
1555
|
+
pattern = pattern + "**";
|
|
1556
|
+
}
|
|
1557
|
+
return pattern;
|
|
1558
|
+
}
|
|
1559
|
+
async execute(command2) {
|
|
1560
|
+
const { path: path2, draftMode, standardSlug, ruleId, language } = command2;
|
|
1561
|
+
this.logger.debug(
|
|
1562
|
+
`Starting linting: path="${path2}", draftMode=${!!draftMode}, standardSlug="${standardSlug || "N/A"}", ruleId="${ruleId || "N/A"}", language="${language || "N/A"}"`
|
|
1563
|
+
);
|
|
1564
|
+
const gitRepoRoot = await this.services.gitRemoteUrlService.getGitRepositoryRoot(path2);
|
|
1565
|
+
const files = await this.services.listFiles.listFilesInDirectory(
|
|
1566
|
+
gitRepoRoot,
|
|
1567
|
+
[],
|
|
1568
|
+
["node_modules", "dist", ".min.", ".map.", ".git"]
|
|
1569
|
+
);
|
|
1570
|
+
const { gitRemoteUrl } = await this.services.gitRemoteUrlService.getGitRemoteUrl(gitRepoRoot);
|
|
1571
|
+
const { branches } = await this.services.gitRemoteUrlService.getCurrentBranches(gitRepoRoot);
|
|
1572
|
+
this.logger.debug(
|
|
1573
|
+
`Git repository: url="${gitRemoteUrl}", branches=${JSON.stringify(branches)}, filesCount=${files.length}`
|
|
1574
|
+
);
|
|
1575
|
+
let detectionPrograms;
|
|
1576
|
+
let isDraftMode = false;
|
|
1577
|
+
if (draftMode && standardSlug && ruleId) {
|
|
1578
|
+
isDraftMode = true;
|
|
1579
|
+
const draftProgramsResult = await this.repositories.packmindGateway.getDraftDetectionProgramsForRule(
|
|
1580
|
+
{
|
|
1581
|
+
standardSlug,
|
|
1582
|
+
ruleId,
|
|
1583
|
+
language
|
|
1584
|
+
}
|
|
1585
|
+
);
|
|
1586
|
+
detectionPrograms = {
|
|
1587
|
+
targets: [
|
|
1588
|
+
{
|
|
1589
|
+
name: "Draft Target",
|
|
1590
|
+
path: "/",
|
|
1591
|
+
standards: [
|
|
1592
|
+
{
|
|
1593
|
+
name: standardSlug,
|
|
1594
|
+
slug: standardSlug,
|
|
1595
|
+
scope: draftProgramsResult.scope ? [draftProgramsResult.scope] : [],
|
|
1596
|
+
rules: [
|
|
1597
|
+
{
|
|
1598
|
+
content: draftProgramsResult.ruleContent || "Draft Rule",
|
|
1599
|
+
activeDetectionPrograms: draftProgramsResult.programs.map(
|
|
1600
|
+
(program) => ({
|
|
1601
|
+
language: program.language,
|
|
1602
|
+
detectionProgram: {
|
|
1603
|
+
mode: program.mode,
|
|
1604
|
+
code: program.code,
|
|
1605
|
+
sourceCodeState: program.sourceCodeState
|
|
1606
|
+
}
|
|
1607
|
+
})
|
|
1608
|
+
)
|
|
1609
|
+
}
|
|
1610
|
+
]
|
|
1611
|
+
}
|
|
1612
|
+
]
|
|
1613
|
+
}
|
|
1614
|
+
]
|
|
1615
|
+
};
|
|
1616
|
+
} else if (!draftMode && standardSlug && ruleId) {
|
|
1617
|
+
const activeProgramsResult = await this.repositories.packmindGateway.getActiveDetectionProgramsForRule(
|
|
1618
|
+
{
|
|
1619
|
+
standardSlug,
|
|
1620
|
+
ruleId,
|
|
1621
|
+
language
|
|
1622
|
+
}
|
|
1623
|
+
);
|
|
1624
|
+
detectionPrograms = {
|
|
1625
|
+
targets: [
|
|
1626
|
+
{
|
|
1627
|
+
name: "Active Target",
|
|
1628
|
+
path: "/",
|
|
1629
|
+
standards: [
|
|
1630
|
+
{
|
|
1631
|
+
name: standardSlug,
|
|
1632
|
+
slug: standardSlug,
|
|
1633
|
+
scope: activeProgramsResult.scope ? [activeProgramsResult.scope] : [],
|
|
1634
|
+
rules: [
|
|
1635
|
+
{
|
|
1636
|
+
content: activeProgramsResult.ruleContent || "Active Rule",
|
|
1637
|
+
activeDetectionPrograms: activeProgramsResult.programs.map(
|
|
1638
|
+
(program) => ({
|
|
1639
|
+
language: program.language,
|
|
1640
|
+
detectionProgram: {
|
|
1641
|
+
mode: program.mode,
|
|
1642
|
+
code: program.code,
|
|
1643
|
+
sourceCodeState: program.sourceCodeState
|
|
1644
|
+
}
|
|
1645
|
+
})
|
|
1646
|
+
)
|
|
1647
|
+
}
|
|
1648
|
+
]
|
|
1649
|
+
}
|
|
1650
|
+
]
|
|
1651
|
+
}
|
|
1652
|
+
]
|
|
1653
|
+
};
|
|
1654
|
+
} else {
|
|
1655
|
+
detectionPrograms = await this.repositories.packmindGateway.listExecutionPrograms({
|
|
1656
|
+
gitRemoteUrl,
|
|
1657
|
+
branches
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
this.logger.debug(
|
|
1661
|
+
`Retrieved detection programs: targetsCount=${detectionPrograms.targets.length}`
|
|
1662
|
+
);
|
|
1663
|
+
const violations = [];
|
|
1664
|
+
for (const file of files) {
|
|
1665
|
+
const fileViolations = [];
|
|
1666
|
+
const relativeFilePath = file.path.startsWith(gitRepoRoot) ? file.path.substring(gitRepoRoot.length) : file.path;
|
|
1667
|
+
const normalizedFilePath = relativeFilePath.startsWith("/") ? relativeFilePath : "/" + relativeFilePath;
|
|
1668
|
+
this.logger.debug(
|
|
1669
|
+
`Processing file: absolute="${file.path}", relative="${normalizedFilePath}"`
|
|
1670
|
+
);
|
|
1671
|
+
const fileExtension = this.extractExtensionFromFile(file.path);
|
|
1672
|
+
const fileLanguage = this.resolveProgrammingLanguage(fileExtension);
|
|
1673
|
+
if (!fileLanguage) {
|
|
1674
|
+
continue;
|
|
1675
|
+
}
|
|
1676
|
+
const programsByLanguage = /* @__PURE__ */ new Map();
|
|
1677
|
+
for (const target of detectionPrograms.targets) {
|
|
1678
|
+
this.logger.debug(
|
|
1679
|
+
`Processing target: name="${target.name}", path="${target.path}", standardsCount=${target.standards.length}`
|
|
1680
|
+
);
|
|
1681
|
+
for (const standard of target.standards) {
|
|
1682
|
+
this.logger.debug(
|
|
1683
|
+
`Checking standard: name="${standard.name}", scope=${JSON.stringify(standard.scope)}, rulesCount=${standard.rules.length}`
|
|
1684
|
+
);
|
|
1685
|
+
if (!this.fileMatchesTargetAndScope(
|
|
1686
|
+
normalizedFilePath,
|
|
1687
|
+
target.path,
|
|
1688
|
+
standard.scope
|
|
1689
|
+
)) {
|
|
1690
|
+
this.logger.debug(
|
|
1691
|
+
`File "${normalizedFilePath}" does not match target/scope - skipping standard "${standard.name}"`
|
|
1692
|
+
);
|
|
1693
|
+
continue;
|
|
1694
|
+
}
|
|
1695
|
+
if (!isDraftMode) {
|
|
1696
|
+
this.logger.debug(
|
|
1697
|
+
`File "${normalizedFilePath}" matches target/scope - processing standard "${standard.name}"`
|
|
1698
|
+
);
|
|
1699
|
+
}
|
|
1700
|
+
for (const rule of standard.rules) {
|
|
1701
|
+
for (const activeProgram of rule.activeDetectionPrograms) {
|
|
1702
|
+
try {
|
|
1703
|
+
const programLanguage = this.resolveProgrammingLanguage(
|
|
1704
|
+
activeProgram.language
|
|
1705
|
+
);
|
|
1706
|
+
if (!programLanguage) {
|
|
1707
|
+
console.error(
|
|
1708
|
+
`Unsupported language "${activeProgram.language}" for file ${file.path}`
|
|
1709
|
+
);
|
|
1710
|
+
continue;
|
|
1711
|
+
}
|
|
1712
|
+
if (programLanguage !== fileLanguage) {
|
|
1713
|
+
continue;
|
|
1714
|
+
}
|
|
1715
|
+
const programsForLanguage = programsByLanguage.get(programLanguage) ?? [];
|
|
1716
|
+
programsForLanguage.push({
|
|
1717
|
+
code: activeProgram.detectionProgram.code,
|
|
1718
|
+
ruleContent: rule.content,
|
|
1719
|
+
standardSlug: standard.slug,
|
|
1720
|
+
sourceCodeState: activeProgram.detectionProgram.sourceCodeState,
|
|
1721
|
+
language: fileLanguage
|
|
1722
|
+
});
|
|
1723
|
+
programsByLanguage.set(programLanguage, programsForLanguage);
|
|
1724
|
+
} catch (error) {
|
|
1725
|
+
console.error(
|
|
1726
|
+
`Error preparing program for file ${file.path}: ${error}`
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
if (programsByLanguage.size > 0) {
|
|
1734
|
+
try {
|
|
1735
|
+
const fileContent = await this.services.listFiles.readFileContent(
|
|
1736
|
+
file.path
|
|
1737
|
+
);
|
|
1738
|
+
for (const [language2, programs] of programsByLanguage.entries()) {
|
|
1739
|
+
try {
|
|
1740
|
+
const result = await this.executeProgramsForFile({
|
|
1741
|
+
filePath: file.path,
|
|
1742
|
+
fileContent,
|
|
1743
|
+
language: language2,
|
|
1744
|
+
programs
|
|
1745
|
+
});
|
|
1746
|
+
fileViolations.push(...result);
|
|
1747
|
+
} catch (error) {
|
|
1748
|
+
console.error(
|
|
1749
|
+
`Error executing programs for file ${file.path} (${language2}): ${error}`
|
|
1750
|
+
);
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
} catch (error) {
|
|
1754
|
+
console.error(
|
|
1755
|
+
`Error reading file content for ${file.path}: ${error}`
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
if (fileViolations.length > 0) {
|
|
1760
|
+
violations.push({
|
|
1761
|
+
file: file.path,
|
|
1762
|
+
violations: fileViolations
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
const totalViolations = violations.reduce(
|
|
1767
|
+
(sum, violation) => sum + violation.violations.length,
|
|
1768
|
+
0
|
|
1769
|
+
);
|
|
1770
|
+
const standardsChecked = Array.from(
|
|
1771
|
+
new Set(
|
|
1772
|
+
detectionPrograms.targets.flatMap(
|
|
1773
|
+
(target) => target.standards.map((standard) => standard.slug)
|
|
1774
|
+
)
|
|
1775
|
+
)
|
|
1776
|
+
);
|
|
1777
|
+
return {
|
|
1778
|
+
gitRemoteUrl,
|
|
1779
|
+
violations,
|
|
1780
|
+
summary: {
|
|
1781
|
+
totalFiles: files.length,
|
|
1782
|
+
violatedFiles: violations.length,
|
|
1783
|
+
totalViolations,
|
|
1784
|
+
standardsChecked
|
|
1785
|
+
}
|
|
1786
|
+
};
|
|
1787
|
+
}
|
|
1788
|
+
resolveProgrammingLanguage(language) {
|
|
1789
|
+
try {
|
|
1790
|
+
return stringToProgrammingLanguage(language);
|
|
1791
|
+
} catch (error) {
|
|
1792
|
+
return null;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
async executeProgramsForFile(command2) {
|
|
1796
|
+
const result = await this.services.linterExecutionUseCase.execute(command2);
|
|
1797
|
+
return result.violations;
|
|
1798
|
+
}
|
|
1799
|
+
extractExtensionFromFile(filePath) {
|
|
1800
|
+
const lastDotIndex = filePath.lastIndexOf(".");
|
|
1801
|
+
if (lastDotIndex === -1 || lastDotIndex === filePath.length - 1) {
|
|
1802
|
+
return "";
|
|
1803
|
+
}
|
|
1804
|
+
return filePath.substring(lastDotIndex + 1);
|
|
1805
|
+
}
|
|
1806
|
+
};
|
|
1807
|
+
|
|
1808
|
+
// apps/cli/src/infra/repositories/PackmindGateway.ts
|
|
1809
|
+
function decodeApiKey(apiKey) {
|
|
1810
|
+
if (!apiKey) {
|
|
1811
|
+
return {
|
|
1812
|
+
payload: { host: "", jwt: "" },
|
|
1813
|
+
isValid: false,
|
|
1814
|
+
error: "Please set the PACKMIND_API_KEY_V3 environment variable"
|
|
1815
|
+
};
|
|
1816
|
+
}
|
|
1817
|
+
try {
|
|
1818
|
+
const trimmedKey = apiKey.trim();
|
|
1819
|
+
const jsonString = Buffer.from(trimmedKey, "base64").toString("utf-8");
|
|
1820
|
+
const payload = JSON.parse(jsonString);
|
|
1821
|
+
if (!payload.host || typeof payload.host !== "string") {
|
|
1822
|
+
return {
|
|
1823
|
+
payload,
|
|
1824
|
+
isValid: false,
|
|
1825
|
+
error: "Invalid API key: missing or invalid host field"
|
|
1826
|
+
};
|
|
1827
|
+
}
|
|
1828
|
+
if (!payload.jwt || typeof payload.jwt !== "string") {
|
|
1829
|
+
return {
|
|
1830
|
+
payload,
|
|
1831
|
+
isValid: false,
|
|
1832
|
+
error: "Invalid API key: missing or invalid jwt field"
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
return {
|
|
1836
|
+
payload,
|
|
1837
|
+
isValid: true
|
|
1838
|
+
};
|
|
1839
|
+
} catch (error) {
|
|
1840
|
+
return {
|
|
1841
|
+
payload: { host: "", jwt: "" },
|
|
1842
|
+
isValid: false,
|
|
1843
|
+
error: `Failed to decode API key: ${error}`
|
|
1844
|
+
};
|
|
1845
|
+
}
|
|
1846
|
+
}
|
|
1847
|
+
var PackmindGateway = class {
|
|
1848
|
+
constructor(apiKey) {
|
|
1849
|
+
this.apiKey = apiKey;
|
|
1850
|
+
this.listExecutionPrograms = async (params) => {
|
|
1851
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1852
|
+
if (!decodedApiKey.isValid) {
|
|
1853
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1854
|
+
}
|
|
1855
|
+
const { host } = decodedApiKey.payload;
|
|
1856
|
+
const url = `${host}/api/v0/list-detection-program`;
|
|
1857
|
+
const payload = {
|
|
1858
|
+
gitRemoteUrl: params.gitRemoteUrl,
|
|
1859
|
+
branches: params.branches
|
|
1860
|
+
};
|
|
1861
|
+
try {
|
|
1862
|
+
const response = await fetch(url, {
|
|
1863
|
+
method: "POST",
|
|
1864
|
+
headers: {
|
|
1865
|
+
"Content-Type": "application/json",
|
|
1866
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1867
|
+
},
|
|
1868
|
+
body: JSON.stringify(payload)
|
|
1869
|
+
});
|
|
1870
|
+
if (!response.ok) {
|
|
1871
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
1872
|
+
try {
|
|
1873
|
+
const errorBody = await response.json();
|
|
1874
|
+
if (errorBody && errorBody.message) {
|
|
1875
|
+
errorMsg = `${errorBody.message}`;
|
|
1876
|
+
}
|
|
1877
|
+
} catch {
|
|
1878
|
+
}
|
|
1879
|
+
throw new Error(errorMsg);
|
|
1880
|
+
}
|
|
1881
|
+
const result = await response.json();
|
|
1882
|
+
return result;
|
|
1883
|
+
} catch (error) {
|
|
1884
|
+
const err = error;
|
|
1885
|
+
const code = err?.code || err?.cause?.code;
|
|
1886
|
+
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"))) {
|
|
1887
|
+
throw new Error(
|
|
1888
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
1889
|
+
);
|
|
1890
|
+
}
|
|
1891
|
+
throw new Error(
|
|
1892
|
+
`Failed to fetch detection programs: Error: ${err?.message || JSON.stringify(error)}`
|
|
1893
|
+
);
|
|
1894
|
+
}
|
|
1895
|
+
};
|
|
1896
|
+
this.getDraftDetectionProgramsForRule = async (params) => {
|
|
1897
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1898
|
+
if (!decodedApiKey.isValid) {
|
|
1899
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1900
|
+
}
|
|
1901
|
+
const { host } = decodedApiKey.payload;
|
|
1902
|
+
const url = `${host}/api/v0/list-draft-detection-program`;
|
|
1903
|
+
const payload = {
|
|
1904
|
+
standardSlug: params.standardSlug,
|
|
1905
|
+
ruleId: params.ruleId
|
|
1906
|
+
};
|
|
1907
|
+
if (params.language) {
|
|
1908
|
+
payload.language = params.language;
|
|
1909
|
+
}
|
|
1910
|
+
try {
|
|
1911
|
+
const response = await fetch(url, {
|
|
1912
|
+
method: "POST",
|
|
1913
|
+
headers: {
|
|
1914
|
+
"Content-Type": "application/json",
|
|
1915
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1916
|
+
},
|
|
1917
|
+
body: JSON.stringify(payload)
|
|
1918
|
+
});
|
|
1919
|
+
if (!response.ok) {
|
|
1920
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
1921
|
+
try {
|
|
1922
|
+
const errorBody = await response.json();
|
|
1923
|
+
if (errorBody && errorBody.message) {
|
|
1924
|
+
errorMsg = `${errorBody.message}`;
|
|
1925
|
+
}
|
|
1926
|
+
} catch {
|
|
1927
|
+
}
|
|
1928
|
+
throw new Error(errorMsg);
|
|
1929
|
+
}
|
|
1930
|
+
const result = await response.json();
|
|
1931
|
+
if (result.programs.length === 0) {
|
|
1932
|
+
const languageMsg = params.language ? ` for language ${params.language}` : "";
|
|
1933
|
+
throw new Error(
|
|
1934
|
+
`No draft detection programs found for rule ${params.ruleId} in standard ${params.standardSlug}${languageMsg}`
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
const transformedResult = {
|
|
1938
|
+
programs: result.programs.map((program) => ({
|
|
1939
|
+
language: program.language,
|
|
1940
|
+
code: program.code,
|
|
1941
|
+
mode: program.mode,
|
|
1942
|
+
sourceCodeState: program.sourceCodeState
|
|
1943
|
+
})),
|
|
1944
|
+
ruleContent: result.ruleContent,
|
|
1945
|
+
standardSlug: params.standardSlug,
|
|
1946
|
+
scope: result.scope
|
|
1947
|
+
};
|
|
1948
|
+
return transformedResult;
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
const err = error;
|
|
1951
|
+
const code = err?.code || err?.cause?.code;
|
|
1952
|
+
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"))) {
|
|
1953
|
+
throw new Error(
|
|
1954
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
throw new Error(
|
|
1958
|
+
`Failed to fetch draft detection programs: Error: ${err?.message || JSON.stringify(error)}`
|
|
1959
|
+
);
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
this.getActiveDetectionProgramsForRule = async (params) => {
|
|
1963
|
+
const decodedApiKey = decodeApiKey(this.apiKey);
|
|
1964
|
+
if (!decodedApiKey.isValid) {
|
|
1965
|
+
throw new Error(`Invalid API key: ${decodedApiKey.error}`);
|
|
1966
|
+
}
|
|
1967
|
+
const { host } = decodedApiKey.payload;
|
|
1968
|
+
const url = `${host}/api/v0/list-active-detection-program`;
|
|
1969
|
+
const payload = {
|
|
1970
|
+
standardSlug: params.standardSlug,
|
|
1971
|
+
ruleId: params.ruleId
|
|
1972
|
+
};
|
|
1973
|
+
if (params.language) {
|
|
1974
|
+
payload.language = params.language;
|
|
1975
|
+
}
|
|
1976
|
+
try {
|
|
1977
|
+
const response = await fetch(url, {
|
|
1978
|
+
method: "POST",
|
|
1979
|
+
headers: {
|
|
1980
|
+
"Content-Type": "application/json",
|
|
1981
|
+
Authorization: `Bearer ${this.apiKey}`
|
|
1982
|
+
},
|
|
1983
|
+
body: JSON.stringify(payload)
|
|
1984
|
+
});
|
|
1985
|
+
if (!response.ok) {
|
|
1986
|
+
let errorMsg = `API request failed: ${response.status} ${response.statusText}`;
|
|
1987
|
+
try {
|
|
1988
|
+
const errorBody = await response.json();
|
|
1989
|
+
if (errorBody && errorBody.message) {
|
|
1990
|
+
errorMsg = `${errorBody.message}`;
|
|
1991
|
+
}
|
|
1992
|
+
} catch {
|
|
1993
|
+
}
|
|
1994
|
+
throw new Error(errorMsg);
|
|
1995
|
+
}
|
|
1996
|
+
const result = await response.json();
|
|
1997
|
+
if (result.programs.length === 0) {
|
|
1998
|
+
const languageMsg = params.language ? ` for language ${params.language}` : "";
|
|
1999
|
+
throw new Error(
|
|
2000
|
+
`No active detection programs found for rule ${params.ruleId} in standard ${params.standardSlug}${languageMsg}`
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
2003
|
+
const transformedResult = {
|
|
2004
|
+
programs: result.programs.map((program) => ({
|
|
2005
|
+
language: program.language,
|
|
2006
|
+
code: program.code,
|
|
2007
|
+
mode: program.mode,
|
|
2008
|
+
sourceCodeState: program.sourceCodeState
|
|
2009
|
+
})),
|
|
2010
|
+
ruleContent: result.ruleContent,
|
|
2011
|
+
standardSlug: params.standardSlug,
|
|
2012
|
+
scope: result.scope
|
|
2013
|
+
};
|
|
2014
|
+
return transformedResult;
|
|
2015
|
+
} catch (error) {
|
|
2016
|
+
const err = error;
|
|
2017
|
+
const code = err?.code || err?.cause?.code;
|
|
2018
|
+
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"))) {
|
|
2019
|
+
throw new Error(
|
|
2020
|
+
`Packmind server is not accessible at ${host}. Please check your network connection or the server URL.`
|
|
2021
|
+
);
|
|
2022
|
+
}
|
|
2023
|
+
throw new Error(
|
|
2024
|
+
`Failed to fetch active detection programs: Error: ${err?.message || JSON.stringify(error)}`
|
|
2025
|
+
);
|
|
2026
|
+
}
|
|
2027
|
+
};
|
|
2028
|
+
}
|
|
2029
|
+
};
|
|
2030
|
+
|
|
2031
|
+
// packages/linter-ast/src/core/ParserError.ts
|
|
2032
|
+
var ParserNotAvailableError = class extends Error {
|
|
2033
|
+
constructor(language, cause) {
|
|
2034
|
+
super(`Parser for ${language} not available`);
|
|
2035
|
+
this.name = "ParserNotAvailableError";
|
|
2036
|
+
this.originalError = cause;
|
|
2037
|
+
}
|
|
2038
|
+
};
|
|
2039
|
+
var ParserInitializationError = class extends Error {
|
|
2040
|
+
constructor(language, message, cause) {
|
|
2041
|
+
super(`Failed to initialize parser for ${language}: ${message}`);
|
|
2042
|
+
this.name = "ParserInitializationError";
|
|
2043
|
+
this.originalError = cause;
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
|
|
2047
|
+
// packages/linter-ast/src/core/BaseParser.ts
|
|
2048
|
+
var import_path = require("path");
|
|
2049
|
+
var import_fs = require("fs");
|
|
2050
|
+
var BaseParser = class _BaseParser {
|
|
2051
|
+
constructor() {
|
|
2052
|
+
this.initialized = false;
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Helper to locate tree-sitter WASM files for CLI executable
|
|
2056
|
+
*/
|
|
2057
|
+
static getTreeSitterWasmPaths() {
|
|
2058
|
+
const execDir = process.argv[0] ? (0, import_path.dirname)(process.argv[0]) : process.cwd();
|
|
2059
|
+
const scriptDir = require.main?.filename ? (0, import_path.dirname)(require.main.filename) : process.cwd();
|
|
2060
|
+
return [
|
|
2061
|
+
// Next to the main script (for npm packages like @packmind/scan)
|
|
2062
|
+
scriptDir,
|
|
2063
|
+
(0, import_path.join)(scriptDir, "tree-sitter"),
|
|
2064
|
+
// Next to the Bun executable in tree-sitter/ subdirectory
|
|
2065
|
+
(0, import_path.join)(execDir, "tree-sitter"),
|
|
2066
|
+
// Fallback paths
|
|
2067
|
+
(0, import_path.join)(process.cwd(), "tree-sitter"),
|
|
2068
|
+
(0, import_path.join)(process.cwd(), "dist/apps/cli-executables/tree-sitter"),
|
|
2069
|
+
(0, import_path.resolve)(__dirname, "tree-sitter"),
|
|
2070
|
+
(0, import_path.resolve)(__dirname, "../../res"),
|
|
2071
|
+
(0, import_path.resolve)(__dirname, "../../../packages/linter-ast/res")
|
|
2072
|
+
];
|
|
2073
|
+
}
|
|
2074
|
+
/**
|
|
2075
|
+
* Get the locateFile function for TreeSitter Parser initialization
|
|
2076
|
+
*/
|
|
2077
|
+
static getTreeSitterLocateFile() {
|
|
2078
|
+
const wasmDirs = _BaseParser.getTreeSitterWasmPaths();
|
|
2079
|
+
return (fileName) => {
|
|
2080
|
+
for (const dir of wasmDirs) {
|
|
2081
|
+
const fullPath = (0, import_path.join)(dir, fileName);
|
|
2082
|
+
if ((0, import_fs.existsSync)(fullPath)) {
|
|
2083
|
+
return fullPath;
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
return (0, import_path.join)(process.cwd(), fileName);
|
|
2087
|
+
};
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Get all possible paths for a language-specific WASM file
|
|
2091
|
+
*/
|
|
2092
|
+
static getLanguageWasmPaths(languageName) {
|
|
2093
|
+
const wasmDirs = _BaseParser.getTreeSitterWasmPaths();
|
|
2094
|
+
const wasmFileName = `tree-sitter-${languageName}.wasm`;
|
|
2095
|
+
return wasmDirs.map((dir) => (0, import_path.join)(dir, wasmFileName)).concat([
|
|
2096
|
+
// Additional fallback paths
|
|
2097
|
+
(0, import_path.resolve)(__dirname, `tree-sitter/${wasmFileName}`),
|
|
2098
|
+
(0, import_path.resolve)(__dirname, wasmFileName),
|
|
2099
|
+
(0, import_path.resolve)(__dirname, `res/${wasmFileName}`),
|
|
2100
|
+
(0, import_path.resolve)(__dirname, `../../res/${wasmFileName}`),
|
|
2101
|
+
(0, import_path.resolve)(__dirname, `../res/${wasmFileName}`),
|
|
2102
|
+
(0, import_path.resolve)(__dirname, `../../../packages/linter-ast/res/${wasmFileName}`),
|
|
2103
|
+
(0, import_path.join)(process.cwd(), `packages/linter-ast/res/${wasmFileName}`)
|
|
2104
|
+
]);
|
|
2105
|
+
}
|
|
2106
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2107
|
+
nodeToJson(node) {
|
|
2108
|
+
return {
|
|
2109
|
+
type: node.type,
|
|
2110
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2111
|
+
line: node.startPosition.row,
|
|
2112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2113
|
+
children: node.children.map((child) => this.nodeToJson(child))
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
};
|
|
2117
|
+
|
|
2118
|
+
// packages/linter-ast/src/parsers/TypeScriptParser.ts
|
|
2119
|
+
var TreeSitter = __toESM(require("web-tree-sitter"));
|
|
2120
|
+
var import_fs2 = require("fs");
|
|
2121
|
+
var TypeScriptParser = class extends BaseParser {
|
|
2122
|
+
async initialize() {
|
|
2123
|
+
try {
|
|
2124
|
+
let lang = null;
|
|
2125
|
+
await TreeSitter.Parser.init({
|
|
2126
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2127
|
+
});
|
|
2128
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("typescript");
|
|
2129
|
+
for (const wasmPath of wasmPaths) {
|
|
2130
|
+
if ((0, import_fs2.existsSync)(wasmPath)) {
|
|
2131
|
+
try {
|
|
2132
|
+
lang = await TreeSitter.Language.load(wasmPath);
|
|
2133
|
+
break;
|
|
2134
|
+
} catch {
|
|
2135
|
+
continue;
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
if (!lang) {
|
|
2140
|
+
throw new Error(
|
|
2141
|
+
`Failed to load tree-sitter-typescript WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
this.parser = new TreeSitter.Parser();
|
|
2145
|
+
this.parser.setLanguage(lang);
|
|
2146
|
+
this.initialized = true;
|
|
2147
|
+
} catch (error) {
|
|
2148
|
+
throw new ParserInitializationError(
|
|
2149
|
+
"typescript",
|
|
2150
|
+
"Failed to load TypeScript WASM parser module",
|
|
2151
|
+
error
|
|
2152
|
+
);
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
async parse(sourceCode) {
|
|
2156
|
+
if (!this.initialized) {
|
|
2157
|
+
await this.initialize();
|
|
2158
|
+
}
|
|
2159
|
+
if (!this.parser) {
|
|
2160
|
+
throw new ParserInitializationError(
|
|
2161
|
+
"typescript",
|
|
2162
|
+
"Parser not initialized"
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
const tree = this.parser.parse(sourceCode);
|
|
2166
|
+
if (!tree) {
|
|
2167
|
+
throw new ParserInitializationError(
|
|
2168
|
+
"typescript",
|
|
2169
|
+
"Failed to parse source code"
|
|
2170
|
+
);
|
|
2171
|
+
}
|
|
2172
|
+
return this.nodeToJson(tree.rootNode);
|
|
2173
|
+
}
|
|
2174
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2175
|
+
async parseRaw(sourceCode) {
|
|
2176
|
+
if (!this.initialized) {
|
|
2177
|
+
await this.initialize();
|
|
2178
|
+
}
|
|
2179
|
+
if (!this.parser) {
|
|
2180
|
+
throw new ParserInitializationError(
|
|
2181
|
+
"typescript",
|
|
2182
|
+
"Parser not initialized"
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
const tree = this.parser.parse(sourceCode);
|
|
2186
|
+
if (!tree) {
|
|
2187
|
+
throw new ParserInitializationError(
|
|
2188
|
+
"typescript",
|
|
2189
|
+
"Failed to parse source code"
|
|
2190
|
+
);
|
|
2191
|
+
}
|
|
2192
|
+
return tree;
|
|
2193
|
+
}
|
|
2194
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2195
|
+
nodeToJson(node) {
|
|
2196
|
+
return {
|
|
2197
|
+
type: node.type,
|
|
2198
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2199
|
+
line: node.startPosition.row + 1,
|
|
2200
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2201
|
+
const child = node.child(i);
|
|
2202
|
+
return child ? this.nodeToJson(child) : null;
|
|
2203
|
+
}).filter((c) => c !== null)
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
getLanguage() {
|
|
2207
|
+
return "typescript";
|
|
2208
|
+
}
|
|
2209
|
+
};
|
|
2210
|
+
|
|
2211
|
+
// packages/linter-ast/src/parsers/JavaScriptParser.ts
|
|
2212
|
+
var TreeSitter2 = __toESM(require("web-tree-sitter"));
|
|
2213
|
+
var import_fs3 = require("fs");
|
|
2214
|
+
var JavaScriptParser = class extends BaseParser {
|
|
2215
|
+
async initialize() {
|
|
2216
|
+
try {
|
|
2217
|
+
let lang = null;
|
|
2218
|
+
await TreeSitter2.Parser.init({
|
|
2219
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2220
|
+
});
|
|
2221
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("javascript");
|
|
2222
|
+
for (const wasmPath of wasmPaths) {
|
|
2223
|
+
if ((0, import_fs3.existsSync)(wasmPath)) {
|
|
2224
|
+
try {
|
|
2225
|
+
lang = await TreeSitter2.Language.load(wasmPath);
|
|
2226
|
+
break;
|
|
2227
|
+
} catch {
|
|
2228
|
+
continue;
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}
|
|
2232
|
+
if (!lang) {
|
|
2233
|
+
throw new Error(
|
|
2234
|
+
`Failed to load tree-sitter-javascript WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2235
|
+
);
|
|
2236
|
+
}
|
|
2237
|
+
this.parser = new TreeSitter2.Parser();
|
|
2238
|
+
this.parser.setLanguage(lang);
|
|
2239
|
+
this.initialized = true;
|
|
2240
|
+
} catch (error) {
|
|
2241
|
+
throw new ParserInitializationError(
|
|
2242
|
+
"javascript",
|
|
2243
|
+
"Failed to load JavaScript WASM parser module",
|
|
2244
|
+
error
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
async parse(sourceCode) {
|
|
2249
|
+
if (!this.initialized) {
|
|
2250
|
+
await this.initialize();
|
|
2251
|
+
}
|
|
2252
|
+
if (!this.parser) {
|
|
2253
|
+
throw new ParserInitializationError(
|
|
2254
|
+
"javascript",
|
|
2255
|
+
"Parser not initialized"
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2258
|
+
const tree = this.parser.parse(sourceCode);
|
|
2259
|
+
if (!tree) {
|
|
2260
|
+
throw new ParserInitializationError(
|
|
2261
|
+
"javascript",
|
|
2262
|
+
"Failed to parse source code"
|
|
2263
|
+
);
|
|
2264
|
+
}
|
|
2265
|
+
return this.nodeToJson(tree.rootNode);
|
|
2266
|
+
}
|
|
2267
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2268
|
+
async parseRaw(sourceCode) {
|
|
2269
|
+
if (!this.initialized) {
|
|
2270
|
+
await this.initialize();
|
|
2271
|
+
}
|
|
2272
|
+
if (!this.parser) {
|
|
2273
|
+
throw new ParserInitializationError(
|
|
2274
|
+
"javascript",
|
|
2275
|
+
"Parser not initialized"
|
|
2276
|
+
);
|
|
2277
|
+
}
|
|
2278
|
+
const tree = this.parser.parse(sourceCode);
|
|
2279
|
+
if (!tree) {
|
|
2280
|
+
throw new ParserInitializationError(
|
|
2281
|
+
"javascript",
|
|
2282
|
+
"Failed to parse source code"
|
|
2283
|
+
);
|
|
2284
|
+
}
|
|
2285
|
+
return tree;
|
|
2286
|
+
}
|
|
2287
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2288
|
+
nodeToJson(node) {
|
|
2289
|
+
return {
|
|
2290
|
+
type: node.type,
|
|
2291
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2292
|
+
line: node.startPosition.row + 1,
|
|
2293
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2294
|
+
const child = node.child(i);
|
|
2295
|
+
return child ? this.nodeToJson(child) : null;
|
|
2296
|
+
}).filter((c) => c !== null)
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
getLanguage() {
|
|
2300
|
+
return "javascript";
|
|
2301
|
+
}
|
|
2302
|
+
};
|
|
2303
|
+
|
|
2304
|
+
// packages/linter-ast/src/parsers/CPPParser.ts
|
|
2305
|
+
var TreeSitter3 = __toESM(require("web-tree-sitter"));
|
|
2306
|
+
var import_fs4 = require("fs");
|
|
2307
|
+
var CPPParser = class extends BaseParser {
|
|
2308
|
+
async initialize() {
|
|
2309
|
+
try {
|
|
2310
|
+
let lang = null;
|
|
2311
|
+
await TreeSitter3.Parser.init({
|
|
2312
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2313
|
+
});
|
|
2314
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("cpp");
|
|
2315
|
+
for (const wasmPath of wasmPaths) {
|
|
2316
|
+
if ((0, import_fs4.existsSync)(wasmPath)) {
|
|
2317
|
+
try {
|
|
2318
|
+
lang = await TreeSitter3.Language.load(wasmPath);
|
|
2319
|
+
break;
|
|
2320
|
+
} catch {
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
}
|
|
2325
|
+
if (!lang) {
|
|
2326
|
+
throw new Error(
|
|
2327
|
+
`Failed to load tree-sitter-cpp WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2328
|
+
);
|
|
2329
|
+
}
|
|
2330
|
+
this.parser = new TreeSitter3.Parser();
|
|
2331
|
+
this.parser.setLanguage(lang);
|
|
2332
|
+
this.initialized = true;
|
|
2333
|
+
} catch (error) {
|
|
2334
|
+
throw new ParserInitializationError(
|
|
2335
|
+
"cpp",
|
|
2336
|
+
"Failed to load C++ WASM parser module",
|
|
2337
|
+
error
|
|
2338
|
+
);
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
async parse(sourceCode) {
|
|
2342
|
+
if (!this.initialized) {
|
|
2343
|
+
await this.initialize();
|
|
2344
|
+
}
|
|
2345
|
+
if (!this.parser) {
|
|
2346
|
+
throw new ParserInitializationError("cpp", "Parser not initialized");
|
|
2347
|
+
}
|
|
2348
|
+
const tree = this.parser.parse(sourceCode);
|
|
2349
|
+
if (!tree) {
|
|
2350
|
+
throw new ParserInitializationError("cpp", "Failed to parse source code");
|
|
2351
|
+
}
|
|
2352
|
+
return this.nodeToJson(tree.rootNode);
|
|
2353
|
+
}
|
|
2354
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2355
|
+
async parseRaw(sourceCode) {
|
|
2356
|
+
if (!this.initialized) {
|
|
2357
|
+
await this.initialize();
|
|
2358
|
+
}
|
|
2359
|
+
if (!this.parser) {
|
|
2360
|
+
throw new ParserInitializationError("cpp", "Parser not initialized");
|
|
2361
|
+
}
|
|
2362
|
+
const tree = this.parser.parse(sourceCode);
|
|
2363
|
+
if (!tree) {
|
|
2364
|
+
throw new ParserInitializationError("cpp", "Failed to parse source code");
|
|
2365
|
+
}
|
|
2366
|
+
return tree;
|
|
2367
|
+
}
|
|
2368
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2369
|
+
nodeToJson(node) {
|
|
2370
|
+
return {
|
|
2371
|
+
type: node.type,
|
|
2372
|
+
text: node.type === "program" || node.type === "binary_expression" || node.type === "translation_unit" ? "<skipped>" : node.text,
|
|
2373
|
+
line: node.startPosition.row + 1,
|
|
2374
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2375
|
+
const child = node.child(i);
|
|
2376
|
+
return child ? this.nodeToJson(child) : null;
|
|
2377
|
+
}).filter((c) => c !== null)
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
getLanguage() {
|
|
2381
|
+
return "cpp";
|
|
2382
|
+
}
|
|
2383
|
+
};
|
|
2384
|
+
|
|
2385
|
+
// packages/linter-ast/src/parsers/GoParser.ts
|
|
2386
|
+
var TreeSitter4 = __toESM(require("web-tree-sitter"));
|
|
2387
|
+
var import_fs5 = require("fs");
|
|
2388
|
+
var GoParser = class extends BaseParser {
|
|
2389
|
+
async initialize() {
|
|
2390
|
+
try {
|
|
2391
|
+
let lang = null;
|
|
2392
|
+
await TreeSitter4.Parser.init({
|
|
2393
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2394
|
+
});
|
|
2395
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("go");
|
|
2396
|
+
for (const wasmPath of wasmPaths) {
|
|
2397
|
+
if ((0, import_fs5.existsSync)(wasmPath)) {
|
|
2398
|
+
try {
|
|
2399
|
+
lang = await TreeSitter4.Language.load(wasmPath);
|
|
2400
|
+
break;
|
|
2401
|
+
} catch {
|
|
2402
|
+
continue;
|
|
2403
|
+
}
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
if (!lang) {
|
|
2407
|
+
throw new Error(
|
|
2408
|
+
`Failed to load tree-sitter-go WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2409
|
+
);
|
|
2410
|
+
}
|
|
2411
|
+
this.parser = new TreeSitter4.Parser();
|
|
2412
|
+
this.parser.setLanguage(lang);
|
|
2413
|
+
this.initialized = true;
|
|
2414
|
+
} catch (error) {
|
|
2415
|
+
throw new ParserInitializationError(
|
|
2416
|
+
"go",
|
|
2417
|
+
"Failed to load Go WASM parser module",
|
|
2418
|
+
error
|
|
2419
|
+
);
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
async parse(sourceCode) {
|
|
2423
|
+
if (!this.initialized) {
|
|
2424
|
+
await this.initialize();
|
|
2425
|
+
}
|
|
2426
|
+
if (!this.parser) {
|
|
2427
|
+
throw new ParserInitializationError("go", "Parser not initialized");
|
|
2428
|
+
}
|
|
2429
|
+
const tree = this.parser.parse(sourceCode);
|
|
2430
|
+
if (!tree) {
|
|
2431
|
+
throw new ParserInitializationError("go", "Failed to parse source code");
|
|
2432
|
+
}
|
|
2433
|
+
return this.nodeToJson(tree.rootNode);
|
|
2434
|
+
}
|
|
2435
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2436
|
+
async parseRaw(sourceCode) {
|
|
2437
|
+
if (!this.initialized) {
|
|
2438
|
+
await this.initialize();
|
|
2439
|
+
}
|
|
2440
|
+
if (!this.parser) {
|
|
2441
|
+
throw new ParserInitializationError("go", "Parser not initialized");
|
|
2442
|
+
}
|
|
2443
|
+
const tree = this.parser.parse(sourceCode);
|
|
2444
|
+
if (!tree) {
|
|
2445
|
+
throw new ParserInitializationError("go", "Failed to parse source code");
|
|
2446
|
+
}
|
|
2447
|
+
return tree;
|
|
2448
|
+
}
|
|
2449
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2450
|
+
nodeToJson(node) {
|
|
2451
|
+
return {
|
|
2452
|
+
type: node.type,
|
|
2453
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2454
|
+
line: node.startPosition.row + 1,
|
|
2455
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2456
|
+
const child = node.child(i);
|
|
2457
|
+
return child ? this.nodeToJson(child) : null;
|
|
2458
|
+
}).filter((c) => c !== null)
|
|
2459
|
+
};
|
|
2460
|
+
}
|
|
2461
|
+
getLanguage() {
|
|
2462
|
+
return "go";
|
|
2463
|
+
}
|
|
2464
|
+
};
|
|
2465
|
+
|
|
2466
|
+
// packages/linter-ast/src/parsers/KotlinParser.ts
|
|
2467
|
+
var TreeSitter5 = __toESM(require("web-tree-sitter"));
|
|
2468
|
+
var import_fs6 = require("fs");
|
|
2469
|
+
var KotlinParser = class extends BaseParser {
|
|
2470
|
+
async initialize() {
|
|
2471
|
+
try {
|
|
2472
|
+
let lang = null;
|
|
2473
|
+
await TreeSitter5.Parser.init({
|
|
2474
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2475
|
+
});
|
|
2476
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("kotlin");
|
|
2477
|
+
for (const wasmPath of wasmPaths) {
|
|
2478
|
+
if ((0, import_fs6.existsSync)(wasmPath)) {
|
|
2479
|
+
try {
|
|
2480
|
+
lang = await TreeSitter5.Language.load(wasmPath);
|
|
2481
|
+
break;
|
|
2482
|
+
} catch {
|
|
2483
|
+
continue;
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
if (!lang) {
|
|
2488
|
+
throw new Error(
|
|
2489
|
+
`Failed to load tree-sitter-kotlin WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2490
|
+
);
|
|
2491
|
+
}
|
|
2492
|
+
this.parser = new TreeSitter5.Parser();
|
|
2493
|
+
this.parser.setLanguage(lang);
|
|
2494
|
+
this.initialized = true;
|
|
2495
|
+
} catch (error) {
|
|
2496
|
+
throw new ParserInitializationError(
|
|
2497
|
+
"kotlin",
|
|
2498
|
+
"Failed to load Kotlin WASM parser module",
|
|
2499
|
+
error
|
|
2500
|
+
);
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
async parse(sourceCode) {
|
|
2504
|
+
if (!this.initialized) {
|
|
2505
|
+
await this.initialize();
|
|
2506
|
+
}
|
|
2507
|
+
if (!this.parser) {
|
|
2508
|
+
throw new ParserInitializationError("kotlin", "Parser not initialized");
|
|
2509
|
+
}
|
|
2510
|
+
const tree = this.parser.parse(sourceCode);
|
|
2511
|
+
if (!tree) {
|
|
2512
|
+
throw new ParserInitializationError(
|
|
2513
|
+
"kotlin",
|
|
2514
|
+
"Failed to parse source code"
|
|
2515
|
+
);
|
|
2516
|
+
}
|
|
2517
|
+
return this.nodeToJson(tree.rootNode);
|
|
2518
|
+
}
|
|
2519
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2520
|
+
async parseRaw(sourceCode) {
|
|
2521
|
+
if (!this.initialized) {
|
|
2522
|
+
await this.initialize();
|
|
2523
|
+
}
|
|
2524
|
+
if (!this.parser) {
|
|
2525
|
+
throw new ParserInitializationError("kotlin", "Parser not initialized");
|
|
2526
|
+
}
|
|
2527
|
+
const tree = this.parser.parse(sourceCode);
|
|
2528
|
+
if (!tree) {
|
|
2529
|
+
throw new ParserInitializationError(
|
|
2530
|
+
"kotlin",
|
|
2531
|
+
"Failed to parse source code"
|
|
2532
|
+
);
|
|
2533
|
+
}
|
|
2534
|
+
return tree;
|
|
2535
|
+
}
|
|
2536
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2537
|
+
nodeToJson(node) {
|
|
2538
|
+
return {
|
|
2539
|
+
type: node.type,
|
|
2540
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2541
|
+
line: node.startPosition.row + 1,
|
|
2542
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2543
|
+
const child = node.child(i);
|
|
2544
|
+
return child ? this.nodeToJson(child) : null;
|
|
2545
|
+
}).filter((c) => c !== null)
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
getLanguage() {
|
|
2549
|
+
return "kotlin";
|
|
2550
|
+
}
|
|
2551
|
+
};
|
|
2552
|
+
|
|
2553
|
+
// packages/linter-ast/src/parsers/CSSParser.ts
|
|
2554
|
+
var TreeSitter6 = __toESM(require("web-tree-sitter"));
|
|
2555
|
+
var import_fs7 = require("fs");
|
|
2556
|
+
var CSSParser = class extends BaseParser {
|
|
2557
|
+
async initialize() {
|
|
2558
|
+
try {
|
|
2559
|
+
let lang = null;
|
|
2560
|
+
await TreeSitter6.Parser.init({
|
|
2561
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2562
|
+
});
|
|
2563
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("css");
|
|
2564
|
+
for (const wasmPath of wasmPaths) {
|
|
2565
|
+
if ((0, import_fs7.existsSync)(wasmPath)) {
|
|
2566
|
+
try {
|
|
2567
|
+
lang = await TreeSitter6.Language.load(wasmPath);
|
|
2568
|
+
break;
|
|
2569
|
+
} catch {
|
|
2570
|
+
continue;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
if (!lang) {
|
|
2575
|
+
throw new Error(
|
|
2576
|
+
`Failed to load tree-sitter-css.wasm file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2577
|
+
);
|
|
2578
|
+
}
|
|
2579
|
+
this.parser = new TreeSitter6.Parser();
|
|
2580
|
+
this.parser.setLanguage(lang);
|
|
2581
|
+
this.initialized = true;
|
|
2582
|
+
} catch (error) {
|
|
2583
|
+
throw new ParserInitializationError(
|
|
2584
|
+
"css",
|
|
2585
|
+
"Failed to load CSS WASM parser module",
|
|
2586
|
+
error
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
async parse(sourceCode) {
|
|
2591
|
+
if (!this.initialized) {
|
|
2592
|
+
await this.initialize();
|
|
2593
|
+
}
|
|
2594
|
+
if (!this.parser) {
|
|
2595
|
+
throw new ParserInitializationError("css", "Parser not initialized");
|
|
2596
|
+
}
|
|
2597
|
+
const tree = this.parser.parse(sourceCode);
|
|
2598
|
+
if (!tree) {
|
|
2599
|
+
throw new ParserInitializationError("css", "Failed to parse source code");
|
|
2600
|
+
}
|
|
2601
|
+
return this.nodeToJson(tree.rootNode);
|
|
2602
|
+
}
|
|
2603
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2604
|
+
async parseRaw(sourceCode) {
|
|
2605
|
+
if (!this.initialized) {
|
|
2606
|
+
await this.initialize();
|
|
2607
|
+
}
|
|
2608
|
+
if (!this.parser) {
|
|
2609
|
+
throw new ParserInitializationError("css", "Parser not initialized");
|
|
2610
|
+
}
|
|
2611
|
+
const tree = this.parser.parse(sourceCode);
|
|
2612
|
+
if (!tree) {
|
|
2613
|
+
throw new ParserInitializationError("css", "Failed to parse source code");
|
|
2614
|
+
}
|
|
2615
|
+
return tree;
|
|
2616
|
+
}
|
|
2617
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2618
|
+
nodeToJson(node) {
|
|
2619
|
+
return {
|
|
2620
|
+
type: node.type,
|
|
2621
|
+
text: node.type === "program" || node.type === "stylesheet" ? "<skipped>" : node.text,
|
|
2622
|
+
line: node.startPosition.row + 1,
|
|
2623
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2624
|
+
const child = node.child(i);
|
|
2625
|
+
return child ? this.nodeToJson(child) : null;
|
|
2626
|
+
}).filter((c) => c !== null)
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
getLanguage() {
|
|
2630
|
+
return "css";
|
|
2631
|
+
}
|
|
2632
|
+
};
|
|
2633
|
+
|
|
2634
|
+
// packages/linter-ast/src/parsers/CSharpParser.ts
|
|
2635
|
+
var TreeSitter7 = __toESM(require("web-tree-sitter"));
|
|
2636
|
+
var import_fs8 = require("fs");
|
|
2637
|
+
var CSharpParser = class extends BaseParser {
|
|
2638
|
+
async initialize() {
|
|
2639
|
+
try {
|
|
2640
|
+
let lang = null;
|
|
2641
|
+
await TreeSitter7.Parser.init({
|
|
2642
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2643
|
+
});
|
|
2644
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("c_sharp");
|
|
2645
|
+
for (const wasmPath of wasmPaths) {
|
|
2646
|
+
if ((0, import_fs8.existsSync)(wasmPath)) {
|
|
2647
|
+
try {
|
|
2648
|
+
lang = await TreeSitter7.Language.load(wasmPath);
|
|
2649
|
+
break;
|
|
2650
|
+
} catch {
|
|
2651
|
+
continue;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
}
|
|
2655
|
+
if (!lang) {
|
|
2656
|
+
throw new Error(
|
|
2657
|
+
`Failed to load tree-sitter-c_sharp WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2658
|
+
);
|
|
2659
|
+
}
|
|
2660
|
+
this.parser = new TreeSitter7.Parser();
|
|
2661
|
+
this.parser.setLanguage(lang);
|
|
2662
|
+
this.initialized = true;
|
|
2663
|
+
} catch (error) {
|
|
2664
|
+
throw new ParserInitializationError(
|
|
2665
|
+
"csharp",
|
|
2666
|
+
"Failed to load C# WASM parser module",
|
|
2667
|
+
error
|
|
2668
|
+
);
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2671
|
+
async parse(sourceCode) {
|
|
2672
|
+
if (!this.initialized) {
|
|
2673
|
+
await this.initialize();
|
|
2674
|
+
}
|
|
2675
|
+
if (!this.parser) {
|
|
2676
|
+
throw new ParserInitializationError("csharp", "Parser not initialized");
|
|
2677
|
+
}
|
|
2678
|
+
const tree = this.parser.parse(sourceCode);
|
|
2679
|
+
if (!tree) {
|
|
2680
|
+
throw new ParserInitializationError(
|
|
2681
|
+
"csharp",
|
|
2682
|
+
"Failed to parse source code"
|
|
2683
|
+
);
|
|
2684
|
+
}
|
|
2685
|
+
return this.nodeToJson(tree.rootNode);
|
|
2686
|
+
}
|
|
2687
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2688
|
+
async parseRaw(sourceCode) {
|
|
2689
|
+
if (!this.initialized) {
|
|
2690
|
+
await this.initialize();
|
|
2691
|
+
}
|
|
2692
|
+
if (!this.parser) {
|
|
2693
|
+
throw new ParserInitializationError("csharp", "Parser not initialized");
|
|
2694
|
+
}
|
|
2695
|
+
const tree = this.parser.parse(sourceCode);
|
|
2696
|
+
if (!tree) {
|
|
2697
|
+
throw new ParserInitializationError(
|
|
2698
|
+
"csharp",
|
|
2699
|
+
"Failed to parse source code"
|
|
2700
|
+
);
|
|
2701
|
+
}
|
|
2702
|
+
return tree;
|
|
2703
|
+
}
|
|
2704
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2705
|
+
nodeToJson(node) {
|
|
2706
|
+
return {
|
|
2707
|
+
type: node.type,
|
|
2708
|
+
text: node.type === "program" || node.type === "compilation_unit" ? "<skipped>" : node.text,
|
|
2709
|
+
line: node.startPosition.row + 1,
|
|
2710
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2711
|
+
const child = node.child(i);
|
|
2712
|
+
return child ? this.nodeToJson(child) : null;
|
|
2713
|
+
}).filter((c) => c !== null)
|
|
2714
|
+
};
|
|
2715
|
+
}
|
|
2716
|
+
getLanguage() {
|
|
2717
|
+
return "csharp";
|
|
2718
|
+
}
|
|
2719
|
+
};
|
|
2720
|
+
|
|
2721
|
+
// packages/linter-ast/src/parsers/PHPParser.ts
|
|
2722
|
+
var TreeSitter8 = __toESM(require("web-tree-sitter"));
|
|
2723
|
+
var import_fs9 = require("fs");
|
|
2724
|
+
var PHPParser = class extends BaseParser {
|
|
2725
|
+
async initialize() {
|
|
2726
|
+
try {
|
|
2727
|
+
let lang = null;
|
|
2728
|
+
await TreeSitter8.Parser.init({
|
|
2729
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2730
|
+
});
|
|
2731
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("php_only").concat(
|
|
2732
|
+
BaseParser.getLanguageWasmPaths("php")
|
|
2733
|
+
);
|
|
2734
|
+
for (const wasmPath of wasmPaths) {
|
|
2735
|
+
if ((0, import_fs9.existsSync)(wasmPath)) {
|
|
2736
|
+
try {
|
|
2737
|
+
lang = await TreeSitter8.Language.load(wasmPath);
|
|
2738
|
+
break;
|
|
2739
|
+
} catch {
|
|
2740
|
+
continue;
|
|
2741
|
+
}
|
|
2742
|
+
}
|
|
2743
|
+
}
|
|
2744
|
+
if (!lang) {
|
|
2745
|
+
throw new Error(
|
|
2746
|
+
`Failed to load tree-sitter-php WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2749
|
+
this.parser = new TreeSitter8.Parser();
|
|
2750
|
+
this.parser.setLanguage(lang);
|
|
2751
|
+
this.initialized = true;
|
|
2752
|
+
} catch (error) {
|
|
2753
|
+
throw new ParserInitializationError(
|
|
2754
|
+
"php",
|
|
2755
|
+
"Failed to load PHP WASM parser module",
|
|
2756
|
+
error
|
|
2757
|
+
);
|
|
2758
|
+
}
|
|
2759
|
+
}
|
|
2760
|
+
async parse(sourceCode) {
|
|
2761
|
+
if (!this.initialized) {
|
|
2762
|
+
await this.initialize();
|
|
2763
|
+
}
|
|
2764
|
+
if (!this.parser) {
|
|
2765
|
+
throw new ParserInitializationError("php", "Parser not initialized");
|
|
2766
|
+
}
|
|
2767
|
+
const tree = this.parser.parse(sourceCode);
|
|
2768
|
+
if (!tree) {
|
|
2769
|
+
throw new ParserInitializationError("php", "Failed to parse source code");
|
|
2770
|
+
}
|
|
2771
|
+
const astJson = this.nodeToJson(tree.rootNode);
|
|
2772
|
+
return astJson;
|
|
2773
|
+
}
|
|
2774
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2775
|
+
async parseRaw(sourceCode) {
|
|
2776
|
+
if (!this.initialized) {
|
|
2777
|
+
await this.initialize();
|
|
2778
|
+
}
|
|
2779
|
+
if (!this.parser) {
|
|
2780
|
+
throw new ParserInitializationError("php", "Parser not initialized");
|
|
2781
|
+
}
|
|
2782
|
+
const tree = this.parser.parse(sourceCode);
|
|
2783
|
+
if (!tree) {
|
|
2784
|
+
throw new ParserInitializationError("php", "Failed to parse source code");
|
|
2785
|
+
}
|
|
2786
|
+
return tree;
|
|
2787
|
+
}
|
|
2788
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2789
|
+
nodeToJson(node) {
|
|
2790
|
+
return {
|
|
2791
|
+
type: node.type,
|
|
2792
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2793
|
+
line: node.startPosition.row + 1,
|
|
2794
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2795
|
+
const child = node.child(i);
|
|
2796
|
+
return child ? this.nodeToJson(child) : null;
|
|
2797
|
+
}).filter((c) => c !== null)
|
|
2798
|
+
};
|
|
2799
|
+
}
|
|
2800
|
+
getLanguage() {
|
|
2801
|
+
return "php";
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
|
|
2805
|
+
// packages/linter-ast/src/parsers/PythonParser.ts
|
|
2806
|
+
var TreeSitter9 = __toESM(require("web-tree-sitter"));
|
|
2807
|
+
var import_fs10 = require("fs");
|
|
2808
|
+
var PythonParser = class extends BaseParser {
|
|
2809
|
+
async initialize() {
|
|
2810
|
+
try {
|
|
2811
|
+
let lang = null;
|
|
2812
|
+
await TreeSitter9.Parser.init({
|
|
2813
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2814
|
+
});
|
|
2815
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("python");
|
|
2816
|
+
for (const wasmPath of wasmPaths) {
|
|
2817
|
+
if ((0, import_fs10.existsSync)(wasmPath)) {
|
|
2818
|
+
try {
|
|
2819
|
+
lang = await TreeSitter9.Language.load(wasmPath);
|
|
2820
|
+
break;
|
|
2821
|
+
} catch {
|
|
2822
|
+
continue;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
}
|
|
2826
|
+
if (!lang) {
|
|
2827
|
+
throw new Error(
|
|
2828
|
+
`Failed to load tree-sitter-python WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2829
|
+
);
|
|
2830
|
+
}
|
|
2831
|
+
this.parser = new TreeSitter9.Parser();
|
|
2832
|
+
this.parser.setLanguage(lang);
|
|
2833
|
+
this.initialized = true;
|
|
2834
|
+
} catch (error) {
|
|
2835
|
+
throw new ParserInitializationError(
|
|
2836
|
+
"python",
|
|
2837
|
+
"Failed to load Python WASM parser module",
|
|
2838
|
+
error
|
|
2839
|
+
);
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2842
|
+
async parse(sourceCode) {
|
|
2843
|
+
if (!this.initialized) {
|
|
2844
|
+
await this.initialize();
|
|
2845
|
+
}
|
|
2846
|
+
if (!this.parser) {
|
|
2847
|
+
throw new ParserInitializationError("python", "Parser not initialized");
|
|
2848
|
+
}
|
|
2849
|
+
const tree = this.parser.parse(sourceCode);
|
|
2850
|
+
if (!tree) {
|
|
2851
|
+
throw new ParserInitializationError(
|
|
2852
|
+
"python",
|
|
2853
|
+
"Failed to parse source code"
|
|
2854
|
+
);
|
|
2855
|
+
}
|
|
2856
|
+
return this.nodeToJson(tree.rootNode);
|
|
2857
|
+
}
|
|
2858
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2859
|
+
async parseRaw(sourceCode) {
|
|
2860
|
+
if (!this.initialized) {
|
|
2861
|
+
await this.initialize();
|
|
2862
|
+
}
|
|
2863
|
+
if (!this.parser) {
|
|
2864
|
+
throw new ParserInitializationError("python", "Parser not initialized");
|
|
2865
|
+
}
|
|
2866
|
+
const tree = this.parser.parse(sourceCode);
|
|
2867
|
+
if (!tree) {
|
|
2868
|
+
throw new ParserInitializationError(
|
|
2869
|
+
"python",
|
|
2870
|
+
"Failed to parse source code"
|
|
2871
|
+
);
|
|
2872
|
+
}
|
|
2873
|
+
return tree;
|
|
2874
|
+
}
|
|
2875
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2876
|
+
nodeToJson(node) {
|
|
2877
|
+
return {
|
|
2878
|
+
type: node.type,
|
|
2879
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2880
|
+
line: node.startPosition.row + 1,
|
|
2881
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2882
|
+
const child = node.child(i);
|
|
2883
|
+
return child ? this.nodeToJson(child) : null;
|
|
2884
|
+
}).filter((c) => c !== null)
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
getLanguage() {
|
|
2888
|
+
return "python";
|
|
2889
|
+
}
|
|
2890
|
+
};
|
|
2891
|
+
|
|
2892
|
+
// packages/linter-ast/src/parsers/RubyParser.ts
|
|
2893
|
+
var TreeSitter10 = __toESM(require("web-tree-sitter"));
|
|
2894
|
+
var import_fs11 = require("fs");
|
|
2895
|
+
var RubyParser = class extends BaseParser {
|
|
2896
|
+
async initialize() {
|
|
2897
|
+
try {
|
|
2898
|
+
let lang = null;
|
|
2899
|
+
await TreeSitter10.Parser.init({
|
|
2900
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2901
|
+
});
|
|
2902
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("ruby");
|
|
2903
|
+
for (const wasmPath of wasmPaths) {
|
|
2904
|
+
if ((0, import_fs11.existsSync)(wasmPath)) {
|
|
2905
|
+
try {
|
|
2906
|
+
lang = await TreeSitter10.Language.load(wasmPath);
|
|
2907
|
+
break;
|
|
2908
|
+
} catch {
|
|
2909
|
+
continue;
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
}
|
|
2913
|
+
if (!lang) {
|
|
2914
|
+
throw new Error(
|
|
2915
|
+
`Failed to load tree-sitter-ruby WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
2916
|
+
);
|
|
2917
|
+
}
|
|
2918
|
+
this.parser = new TreeSitter10.Parser();
|
|
2919
|
+
this.parser.setLanguage(lang);
|
|
2920
|
+
this.initialized = true;
|
|
2921
|
+
} catch (error) {
|
|
2922
|
+
throw new ParserInitializationError(
|
|
2923
|
+
"ruby",
|
|
2924
|
+
"Failed to load Ruby WASM parser module",
|
|
2925
|
+
error
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
async parse(sourceCode) {
|
|
2930
|
+
if (!this.initialized) {
|
|
2931
|
+
await this.initialize();
|
|
2932
|
+
}
|
|
2933
|
+
if (!this.parser) {
|
|
2934
|
+
throw new ParserInitializationError("ruby", "Parser not initialized");
|
|
2935
|
+
}
|
|
2936
|
+
const tree = this.parser.parse(sourceCode);
|
|
2937
|
+
if (!tree) {
|
|
2938
|
+
throw new ParserInitializationError(
|
|
2939
|
+
"ruby",
|
|
2940
|
+
"Failed to parse source code"
|
|
2941
|
+
);
|
|
2942
|
+
}
|
|
2943
|
+
return this.nodeToJson(tree.rootNode);
|
|
2944
|
+
}
|
|
2945
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2946
|
+
async parseRaw(sourceCode) {
|
|
2947
|
+
if (!this.initialized) {
|
|
2948
|
+
await this.initialize();
|
|
2949
|
+
}
|
|
2950
|
+
if (!this.parser) {
|
|
2951
|
+
throw new ParserInitializationError("ruby", "Parser not initialized");
|
|
2952
|
+
}
|
|
2953
|
+
const tree = this.parser.parse(sourceCode);
|
|
2954
|
+
if (!tree) {
|
|
2955
|
+
throw new ParserInitializationError(
|
|
2956
|
+
"ruby",
|
|
2957
|
+
"Failed to parse source code"
|
|
2958
|
+
);
|
|
2959
|
+
}
|
|
2960
|
+
return tree;
|
|
2961
|
+
}
|
|
2962
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2963
|
+
nodeToJson(node) {
|
|
2964
|
+
return {
|
|
2965
|
+
type: node.type,
|
|
2966
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
2967
|
+
line: node.startPosition.row + 1,
|
|
2968
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
2969
|
+
const child = node.child(i);
|
|
2970
|
+
return child ? this.nodeToJson(child) : null;
|
|
2971
|
+
}).filter((c) => c !== null)
|
|
2972
|
+
};
|
|
2973
|
+
}
|
|
2974
|
+
getLanguage() {
|
|
2975
|
+
return "ruby";
|
|
2976
|
+
}
|
|
2977
|
+
};
|
|
2978
|
+
|
|
2979
|
+
// packages/linter-ast/src/parsers/JSONParser.ts
|
|
2980
|
+
var TreeSitter11 = __toESM(require("web-tree-sitter"));
|
|
2981
|
+
var import_fs12 = require("fs");
|
|
2982
|
+
var JSONParser = class extends BaseParser {
|
|
2983
|
+
async initialize() {
|
|
2984
|
+
try {
|
|
2985
|
+
let lang = null;
|
|
2986
|
+
await TreeSitter11.Parser.init({
|
|
2987
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
2988
|
+
});
|
|
2989
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("json");
|
|
2990
|
+
for (const wasmPath of wasmPaths) {
|
|
2991
|
+
if ((0, import_fs12.existsSync)(wasmPath)) {
|
|
2992
|
+
try {
|
|
2993
|
+
lang = await TreeSitter11.Language.load(wasmPath);
|
|
2994
|
+
break;
|
|
2995
|
+
} catch {
|
|
2996
|
+
continue;
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
}
|
|
3000
|
+
if (!lang) {
|
|
3001
|
+
throw new Error(
|
|
3002
|
+
`Failed to load tree-sitter-json WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
3003
|
+
);
|
|
3004
|
+
}
|
|
3005
|
+
this.parser = new TreeSitter11.Parser();
|
|
3006
|
+
this.parser.setLanguage(lang);
|
|
3007
|
+
this.initialized = true;
|
|
3008
|
+
} catch (error) {
|
|
3009
|
+
throw new ParserInitializationError(
|
|
3010
|
+
"json",
|
|
3011
|
+
"Failed to load JSON WASM parser module",
|
|
3012
|
+
error
|
|
3013
|
+
);
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
async parse(sourceCode) {
|
|
3017
|
+
if (!this.initialized) {
|
|
3018
|
+
await this.initialize();
|
|
3019
|
+
}
|
|
3020
|
+
if (!this.parser) {
|
|
3021
|
+
throw new ParserInitializationError("json", "Parser not initialized");
|
|
3022
|
+
}
|
|
3023
|
+
const tree = this.parser.parse(sourceCode);
|
|
3024
|
+
if (!tree) {
|
|
3025
|
+
throw new ParserInitializationError(
|
|
3026
|
+
"json",
|
|
3027
|
+
"Failed to parse source code"
|
|
3028
|
+
);
|
|
3029
|
+
}
|
|
3030
|
+
return this.nodeToJson(tree.rootNode);
|
|
3031
|
+
}
|
|
3032
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3033
|
+
async parseRaw(sourceCode) {
|
|
3034
|
+
if (!this.initialized) {
|
|
3035
|
+
await this.initialize();
|
|
3036
|
+
}
|
|
3037
|
+
if (!this.parser) {
|
|
3038
|
+
throw new ParserInitializationError("json", "Parser not initialized");
|
|
3039
|
+
}
|
|
3040
|
+
const tree = this.parser.parse(sourceCode);
|
|
3041
|
+
if (!tree) {
|
|
3042
|
+
throw new ParserInitializationError(
|
|
3043
|
+
"json",
|
|
3044
|
+
"Failed to parse source code"
|
|
3045
|
+
);
|
|
3046
|
+
}
|
|
3047
|
+
return tree;
|
|
3048
|
+
}
|
|
3049
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3050
|
+
nodeToJson(node) {
|
|
3051
|
+
return {
|
|
3052
|
+
type: node.type,
|
|
3053
|
+
text: node.type === "document" ? "<skipped>" : node.text,
|
|
3054
|
+
line: node.startPosition.row + 1,
|
|
3055
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
3056
|
+
const child = node.child(i);
|
|
3057
|
+
return child ? this.nodeToJson(child) : null;
|
|
3058
|
+
}).filter((c) => c !== null)
|
|
3059
|
+
};
|
|
3060
|
+
}
|
|
3061
|
+
getLanguage() {
|
|
3062
|
+
return "json";
|
|
3063
|
+
}
|
|
3064
|
+
};
|
|
3065
|
+
|
|
3066
|
+
// packages/linter-ast/src/parsers/HTMLParser.ts
|
|
3067
|
+
var TreeSitter12 = __toESM(require("web-tree-sitter"));
|
|
3068
|
+
var import_fs13 = require("fs");
|
|
3069
|
+
var HTMLParser = class extends BaseParser {
|
|
3070
|
+
async initialize() {
|
|
3071
|
+
try {
|
|
3072
|
+
let lang = null;
|
|
3073
|
+
await TreeSitter12.Parser.init({
|
|
3074
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
3075
|
+
});
|
|
3076
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("html");
|
|
3077
|
+
for (const wasmPath of wasmPaths) {
|
|
3078
|
+
if ((0, import_fs13.existsSync)(wasmPath)) {
|
|
3079
|
+
try {
|
|
3080
|
+
lang = await TreeSitter12.Language.load(wasmPath);
|
|
3081
|
+
break;
|
|
3082
|
+
} catch {
|
|
3083
|
+
continue;
|
|
3084
|
+
}
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
if (!lang) {
|
|
3088
|
+
throw new Error(
|
|
3089
|
+
`Failed to load tree-sitter-html WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
3090
|
+
);
|
|
3091
|
+
}
|
|
3092
|
+
this.parser = new TreeSitter12.Parser();
|
|
3093
|
+
this.parser.setLanguage(lang);
|
|
3094
|
+
this.initialized = true;
|
|
3095
|
+
} catch (error) {
|
|
3096
|
+
throw new ParserInitializationError(
|
|
3097
|
+
"html",
|
|
3098
|
+
"Failed to load HTML WASM parser module",
|
|
3099
|
+
error
|
|
3100
|
+
);
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
async parse(sourceCode) {
|
|
3104
|
+
if (!this.initialized) {
|
|
3105
|
+
await this.initialize();
|
|
3106
|
+
}
|
|
3107
|
+
if (!this.parser) {
|
|
3108
|
+
throw new ParserInitializationError("html", "Parser not initialized");
|
|
3109
|
+
}
|
|
3110
|
+
const tree = this.parser.parse(sourceCode);
|
|
3111
|
+
if (!tree) {
|
|
3112
|
+
throw new ParserInitializationError(
|
|
3113
|
+
"html",
|
|
3114
|
+
"Failed to parse source code"
|
|
3115
|
+
);
|
|
3116
|
+
}
|
|
3117
|
+
return this.nodeToJson(tree.rootNode);
|
|
3118
|
+
}
|
|
3119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3120
|
+
async parseRaw(sourceCode) {
|
|
3121
|
+
if (!this.initialized) {
|
|
3122
|
+
await this.initialize();
|
|
3123
|
+
}
|
|
3124
|
+
if (!this.parser) {
|
|
3125
|
+
throw new ParserInitializationError("html", "Parser not initialized");
|
|
3126
|
+
}
|
|
3127
|
+
const tree = this.parser.parse(sourceCode);
|
|
3128
|
+
if (!tree) {
|
|
3129
|
+
throw new ParserInitializationError(
|
|
3130
|
+
"html",
|
|
3131
|
+
"Failed to parse source code"
|
|
3132
|
+
);
|
|
3133
|
+
}
|
|
3134
|
+
return tree;
|
|
3135
|
+
}
|
|
3136
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3137
|
+
nodeToJson(node) {
|
|
3138
|
+
return {
|
|
3139
|
+
type: node.type,
|
|
3140
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
3141
|
+
line: node.startPosition.row + 1,
|
|
3142
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
3143
|
+
const child = node.child(i);
|
|
3144
|
+
return child ? this.nodeToJson(child) : null;
|
|
3145
|
+
}).filter((c) => c !== null)
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
getLanguage() {
|
|
3149
|
+
return "html";
|
|
3150
|
+
}
|
|
3151
|
+
};
|
|
3152
|
+
|
|
3153
|
+
// packages/linter-ast/src/parsers/JavaParser.ts
|
|
3154
|
+
var TreeSitter13 = __toESM(require("web-tree-sitter"));
|
|
3155
|
+
var import_fs14 = require("fs");
|
|
3156
|
+
var JavaParser = class extends BaseParser {
|
|
3157
|
+
async initialize() {
|
|
3158
|
+
try {
|
|
3159
|
+
let lang = null;
|
|
3160
|
+
await TreeSitter13.Parser.init({
|
|
3161
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
3162
|
+
});
|
|
3163
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("java");
|
|
3164
|
+
for (const wasmPath of wasmPaths) {
|
|
3165
|
+
if ((0, import_fs14.existsSync)(wasmPath)) {
|
|
3166
|
+
try {
|
|
3167
|
+
lang = await TreeSitter13.Language.load(wasmPath);
|
|
3168
|
+
break;
|
|
3169
|
+
} catch {
|
|
3170
|
+
continue;
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
3174
|
+
if (!lang) {
|
|
3175
|
+
throw new Error(
|
|
3176
|
+
`Failed to load tree-sitter-java WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
3177
|
+
);
|
|
3178
|
+
}
|
|
3179
|
+
this.parser = new TreeSitter13.Parser();
|
|
3180
|
+
this.parser.setLanguage(lang);
|
|
3181
|
+
this.initialized = true;
|
|
3182
|
+
} catch (error) {
|
|
3183
|
+
throw new ParserInitializationError(
|
|
3184
|
+
"java",
|
|
3185
|
+
"Failed to load Java WASM parser module",
|
|
3186
|
+
error
|
|
3187
|
+
);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
async parse(sourceCode) {
|
|
3191
|
+
if (!this.initialized) {
|
|
3192
|
+
await this.initialize();
|
|
3193
|
+
}
|
|
3194
|
+
if (!this.parser) {
|
|
3195
|
+
throw new ParserInitializationError("java", "Parser not initialized");
|
|
3196
|
+
}
|
|
3197
|
+
const tree = this.parser.parse(sourceCode);
|
|
3198
|
+
if (!tree) {
|
|
3199
|
+
throw new ParserInitializationError(
|
|
3200
|
+
"java",
|
|
3201
|
+
"Failed to parse source code"
|
|
3202
|
+
);
|
|
3203
|
+
}
|
|
3204
|
+
return this.nodeToJson(tree.rootNode);
|
|
3205
|
+
}
|
|
3206
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3207
|
+
async parseRaw(sourceCode) {
|
|
3208
|
+
if (!this.initialized) {
|
|
3209
|
+
await this.initialize();
|
|
3210
|
+
}
|
|
3211
|
+
if (!this.parser) {
|
|
3212
|
+
throw new ParserInitializationError("java", "Parser not initialized");
|
|
3213
|
+
}
|
|
3214
|
+
const tree = this.parser.parse(sourceCode);
|
|
3215
|
+
if (!tree) {
|
|
3216
|
+
throw new ParserInitializationError(
|
|
3217
|
+
"java",
|
|
3218
|
+
"Failed to parse source code"
|
|
3219
|
+
);
|
|
3220
|
+
}
|
|
3221
|
+
return tree;
|
|
3222
|
+
}
|
|
3223
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3224
|
+
nodeToJson(node) {
|
|
3225
|
+
return {
|
|
3226
|
+
type: node.type,
|
|
3227
|
+
text: node.type === "program" ? "<skipped>" : node.text,
|
|
3228
|
+
line: node.startPosition.row + 1,
|
|
3229
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
3230
|
+
const child = node.child(i);
|
|
3231
|
+
return child ? this.nodeToJson(child) : null;
|
|
3232
|
+
}).filter((c) => c !== null)
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
getLanguage() {
|
|
3236
|
+
return "java";
|
|
3237
|
+
}
|
|
3238
|
+
};
|
|
3239
|
+
|
|
3240
|
+
// packages/linter-ast/src/parsers/SwiftParser.ts
|
|
3241
|
+
var TreeSitter14 = __toESM(require("web-tree-sitter"));
|
|
3242
|
+
var import_fs15 = require("fs");
|
|
3243
|
+
var SwiftParser = class extends BaseParser {
|
|
3244
|
+
async initialize() {
|
|
3245
|
+
try {
|
|
3246
|
+
let lang = null;
|
|
3247
|
+
await TreeSitter14.Parser.init({
|
|
3248
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
3249
|
+
});
|
|
3250
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("swift");
|
|
3251
|
+
for (const wasmPath of wasmPaths) {
|
|
3252
|
+
if ((0, import_fs15.existsSync)(wasmPath)) {
|
|
3253
|
+
try {
|
|
3254
|
+
lang = await TreeSitter14.Language.load(wasmPath);
|
|
3255
|
+
break;
|
|
3256
|
+
} catch {
|
|
3257
|
+
continue;
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
}
|
|
3261
|
+
if (!lang) {
|
|
3262
|
+
throw new Error(
|
|
3263
|
+
`Failed to load tree-sitter-swift WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
3264
|
+
);
|
|
3265
|
+
}
|
|
3266
|
+
this.parser = new TreeSitter14.Parser();
|
|
3267
|
+
this.parser.setLanguage(lang);
|
|
3268
|
+
this.initialized = true;
|
|
3269
|
+
} catch (error) {
|
|
3270
|
+
throw new ParserInitializationError(
|
|
3271
|
+
"swift",
|
|
3272
|
+
"Failed to load Swift WASM parser module",
|
|
3273
|
+
error
|
|
3274
|
+
);
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
async parse(sourceCode) {
|
|
3278
|
+
if (!this.initialized) {
|
|
3279
|
+
await this.initialize();
|
|
3280
|
+
}
|
|
3281
|
+
if (!this.parser) {
|
|
3282
|
+
throw new ParserInitializationError("swift", "Parser not initialized");
|
|
3283
|
+
}
|
|
3284
|
+
const tree = this.parser.parse(sourceCode);
|
|
3285
|
+
if (!tree) {
|
|
3286
|
+
throw new ParserInitializationError(
|
|
3287
|
+
"swift",
|
|
3288
|
+
"Failed to parse source code"
|
|
3289
|
+
);
|
|
3290
|
+
}
|
|
3291
|
+
return this.nodeToJson(tree.rootNode);
|
|
3292
|
+
}
|
|
3293
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3294
|
+
async parseRaw(sourceCode) {
|
|
3295
|
+
if (!this.initialized) {
|
|
3296
|
+
await this.initialize();
|
|
3297
|
+
}
|
|
3298
|
+
if (!this.parser) {
|
|
3299
|
+
throw new ParserInitializationError("swift", "Parser not initialized");
|
|
3300
|
+
}
|
|
3301
|
+
const tree = this.parser.parse(sourceCode);
|
|
3302
|
+
if (!tree) {
|
|
3303
|
+
throw new ParserInitializationError(
|
|
3304
|
+
"swift",
|
|
3305
|
+
"Failed to parse source code"
|
|
3306
|
+
);
|
|
3307
|
+
}
|
|
3308
|
+
return tree;
|
|
3309
|
+
}
|
|
3310
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3311
|
+
nodeToJson(node) {
|
|
3312
|
+
return {
|
|
3313
|
+
type: node.type,
|
|
3314
|
+
text: node.type === "program" || node.type === "source_file" ? "<skipped>" : node.text,
|
|
3315
|
+
line: node.startPosition.row + 1,
|
|
3316
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
3317
|
+
const child = node.child(i);
|
|
3318
|
+
return child ? this.nodeToJson(child) : null;
|
|
3319
|
+
}).filter((c) => c !== null)
|
|
3320
|
+
};
|
|
3321
|
+
}
|
|
3322
|
+
getLanguage() {
|
|
3323
|
+
return "swift";
|
|
3324
|
+
}
|
|
3325
|
+
};
|
|
3326
|
+
|
|
3327
|
+
// packages/linter-ast/src/parsers/SCSSParser.ts
|
|
3328
|
+
var TreeSitter15 = __toESM(require("web-tree-sitter"));
|
|
3329
|
+
var import_fs16 = require("fs");
|
|
3330
|
+
var SCSSParser = class extends BaseParser {
|
|
3331
|
+
async initialize() {
|
|
3332
|
+
try {
|
|
3333
|
+
let lang = null;
|
|
3334
|
+
await TreeSitter15.Parser.init({
|
|
3335
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
3336
|
+
});
|
|
3337
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("scss");
|
|
3338
|
+
for (const wasmPath of wasmPaths) {
|
|
3339
|
+
if ((0, import_fs16.existsSync)(wasmPath)) {
|
|
3340
|
+
try {
|
|
3341
|
+
lang = await TreeSitter15.Language.load(wasmPath);
|
|
3342
|
+
break;
|
|
3343
|
+
} catch {
|
|
3344
|
+
continue;
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
if (!lang) {
|
|
3349
|
+
throw new Error(
|
|
3350
|
+
`Failed to load tree-sitter-scss WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
3351
|
+
);
|
|
3352
|
+
}
|
|
3353
|
+
this.parser = new TreeSitter15.Parser();
|
|
3354
|
+
this.parser.setLanguage(lang);
|
|
3355
|
+
this.initialized = true;
|
|
3356
|
+
} catch (error) {
|
|
3357
|
+
throw new ParserInitializationError(
|
|
3358
|
+
"scss",
|
|
3359
|
+
"Failed to load SCSS WASM parser module",
|
|
3360
|
+
error
|
|
3361
|
+
);
|
|
3362
|
+
}
|
|
3363
|
+
}
|
|
3364
|
+
async parse(sourceCode) {
|
|
3365
|
+
if (!this.initialized) {
|
|
3366
|
+
await this.initialize();
|
|
3367
|
+
}
|
|
3368
|
+
if (!this.parser) {
|
|
3369
|
+
throw new ParserInitializationError("scss", "Parser not initialized");
|
|
3370
|
+
}
|
|
3371
|
+
const tree = this.parser.parse(sourceCode);
|
|
3372
|
+
if (!tree) {
|
|
3373
|
+
throw new ParserInitializationError(
|
|
3374
|
+
"scss",
|
|
3375
|
+
"Failed to parse source code"
|
|
3376
|
+
);
|
|
3377
|
+
}
|
|
3378
|
+
return this.nodeToJson(tree.rootNode);
|
|
3379
|
+
}
|
|
3380
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3381
|
+
async parseRaw(sourceCode) {
|
|
3382
|
+
if (!this.initialized) {
|
|
3383
|
+
await this.initialize();
|
|
3384
|
+
}
|
|
3385
|
+
if (!this.parser) {
|
|
3386
|
+
throw new ParserInitializationError("scss", "Parser not initialized");
|
|
3387
|
+
}
|
|
3388
|
+
const tree = this.parser.parse(sourceCode);
|
|
3389
|
+
if (!tree) {
|
|
3390
|
+
throw new ParserInitializationError(
|
|
3391
|
+
"scss",
|
|
3392
|
+
"Failed to parse source code"
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
return tree;
|
|
3396
|
+
}
|
|
3397
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3398
|
+
nodeToJson(node) {
|
|
3399
|
+
return {
|
|
3400
|
+
type: node.type,
|
|
3401
|
+
text: node.type === "program" || node.type === "stylesheet" ? "<skipped>" : node.text,
|
|
3402
|
+
line: node.startPosition.row + 1,
|
|
3403
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
3404
|
+
const child = node.child(i);
|
|
3405
|
+
return child ? this.nodeToJson(child) : null;
|
|
3406
|
+
}).filter((c) => c !== null)
|
|
3407
|
+
};
|
|
3408
|
+
}
|
|
3409
|
+
getLanguage() {
|
|
3410
|
+
return "scss";
|
|
3411
|
+
}
|
|
3412
|
+
};
|
|
3413
|
+
|
|
3414
|
+
// packages/linter-ast/src/parsers/YAMLParser.ts
|
|
3415
|
+
var TreeSitter16 = __toESM(require("web-tree-sitter"));
|
|
3416
|
+
var import_fs17 = require("fs");
|
|
3417
|
+
var YAMLParser = class extends BaseParser {
|
|
3418
|
+
async initialize() {
|
|
3419
|
+
try {
|
|
3420
|
+
let lang = null;
|
|
3421
|
+
await TreeSitter16.Parser.init({
|
|
3422
|
+
locateFile: BaseParser.getTreeSitterLocateFile()
|
|
3423
|
+
});
|
|
3424
|
+
const wasmPaths = BaseParser.getLanguageWasmPaths("yaml");
|
|
3425
|
+
for (const wasmPath of wasmPaths) {
|
|
3426
|
+
if ((0, import_fs17.existsSync)(wasmPath)) {
|
|
3427
|
+
try {
|
|
3428
|
+
lang = await TreeSitter16.Language.load(wasmPath);
|
|
3429
|
+
break;
|
|
3430
|
+
} catch {
|
|
3431
|
+
continue;
|
|
3432
|
+
}
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
3435
|
+
if (!lang) {
|
|
3436
|
+
throw new Error(
|
|
3437
|
+
`Failed to load tree-sitter-yaml WASM file. Tried paths: ${wasmPaths.join(", ")}`
|
|
3438
|
+
);
|
|
3439
|
+
}
|
|
3440
|
+
this.parser = new TreeSitter16.Parser();
|
|
3441
|
+
this.parser.setLanguage(lang);
|
|
3442
|
+
this.initialized = true;
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
throw new ParserInitializationError(
|
|
3445
|
+
"yaml",
|
|
3446
|
+
"Failed to load YAML WASM parser module",
|
|
3447
|
+
error
|
|
3448
|
+
);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
async parse(sourceCode) {
|
|
3452
|
+
if (!this.initialized) {
|
|
3453
|
+
await this.initialize();
|
|
3454
|
+
}
|
|
3455
|
+
if (!this.parser) {
|
|
3456
|
+
throw new ParserInitializationError("yaml", "Parser not initialized");
|
|
3457
|
+
}
|
|
3458
|
+
const tree = this.parser.parse(sourceCode);
|
|
3459
|
+
if (!tree) {
|
|
3460
|
+
throw new ParserInitializationError(
|
|
3461
|
+
"yaml",
|
|
3462
|
+
"Failed to parse source code"
|
|
3463
|
+
);
|
|
3464
|
+
}
|
|
3465
|
+
return this.nodeToJson(tree.rootNode);
|
|
3466
|
+
}
|
|
3467
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3468
|
+
async parseRaw(sourceCode) {
|
|
3469
|
+
if (!this.initialized) {
|
|
3470
|
+
await this.initialize();
|
|
3471
|
+
}
|
|
3472
|
+
if (!this.parser) {
|
|
3473
|
+
throw new ParserInitializationError("yaml", "Parser not initialized");
|
|
3474
|
+
}
|
|
3475
|
+
const tree = this.parser.parse(sourceCode);
|
|
3476
|
+
if (!tree) {
|
|
3477
|
+
throw new ParserInitializationError(
|
|
3478
|
+
"yaml",
|
|
3479
|
+
"Failed to parse source code"
|
|
3480
|
+
);
|
|
3481
|
+
}
|
|
3482
|
+
return tree;
|
|
3483
|
+
}
|
|
3484
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3485
|
+
nodeToJson(node) {
|
|
3486
|
+
return {
|
|
3487
|
+
type: node.type,
|
|
3488
|
+
text: node.type === "stream" ? "<skipped>" : node.text,
|
|
3489
|
+
line: node.startPosition.row + 1,
|
|
3490
|
+
children: Array.from({ length: node.childCount }, (_, i) => {
|
|
3491
|
+
const child = node.child(i);
|
|
3492
|
+
return child ? this.nodeToJson(child) : null;
|
|
3493
|
+
}).filter((c) => c !== null)
|
|
3494
|
+
};
|
|
3495
|
+
}
|
|
3496
|
+
getLanguage() {
|
|
3497
|
+
return "yaml";
|
|
3498
|
+
}
|
|
3499
|
+
};
|
|
3500
|
+
|
|
3501
|
+
// packages/linter-ast/src/core/ParserRegistry.ts
|
|
3502
|
+
var ParserRegistry = class {
|
|
3503
|
+
constructor() {
|
|
3504
|
+
this.parsers = /* @__PURE__ */ new Map();
|
|
3505
|
+
this.parserClasses = {
|
|
3506
|
+
typescript: TypeScriptParser,
|
|
3507
|
+
javascript: JavaScriptParser,
|
|
3508
|
+
cpp: CPPParser,
|
|
3509
|
+
go: GoParser,
|
|
3510
|
+
kotlin: KotlinParser,
|
|
3511
|
+
css: CSSParser,
|
|
3512
|
+
csharp: CSharpParser,
|
|
3513
|
+
php: PHPParser,
|
|
3514
|
+
python: PythonParser,
|
|
3515
|
+
ruby: RubyParser,
|
|
3516
|
+
json: JSONParser,
|
|
3517
|
+
html: HTMLParser,
|
|
3518
|
+
java: JavaParser,
|
|
3519
|
+
swift: SwiftParser,
|
|
3520
|
+
scss: SCSSParser,
|
|
3521
|
+
yaml: YAMLParser
|
|
3522
|
+
};
|
|
3523
|
+
}
|
|
3524
|
+
async getParser(language) {
|
|
3525
|
+
const cached = this.parsers.get(language);
|
|
3526
|
+
if (cached) {
|
|
3527
|
+
return cached;
|
|
3528
|
+
}
|
|
3529
|
+
const ParserClass = this.parserClasses[language];
|
|
3530
|
+
if (!ParserClass) {
|
|
3531
|
+
throw new ParserNotAvailableError(language);
|
|
3532
|
+
}
|
|
3533
|
+
const parser = new ParserClass();
|
|
3534
|
+
await parser.initialize();
|
|
3535
|
+
this.parsers.set(language, parser);
|
|
3536
|
+
return parser;
|
|
3537
|
+
}
|
|
3538
|
+
getAvailableParsers() {
|
|
3539
|
+
return Object.keys(this.parserClasses);
|
|
3540
|
+
}
|
|
3541
|
+
clearCache() {
|
|
3542
|
+
this.parsers.clear();
|
|
3543
|
+
}
|
|
3544
|
+
};
|
|
3545
|
+
|
|
3546
|
+
// packages/linter-ast/src/application/ConsoleLogRemovalService.ts
|
|
3547
|
+
var ConsoleLogRemovalService = class {
|
|
3548
|
+
constructor() {
|
|
3549
|
+
this.jsParser = new JavaScriptParser();
|
|
3550
|
+
}
|
|
3551
|
+
/**
|
|
3552
|
+
* Removes all console method calls from JavaScript source code using AST parsing
|
|
3553
|
+
* @param sourceCode The source code to clean
|
|
3554
|
+
* @param language The programming language (must be JAVASCRIPT)
|
|
3555
|
+
* @returns The cleaned source code with console statements removed
|
|
3556
|
+
* @throws Error if language is not JAVASCRIPT
|
|
3557
|
+
*/
|
|
3558
|
+
async removeConsoleLogStatements(sourceCode, language) {
|
|
3559
|
+
if (language !== "JAVASCRIPT" /* JAVASCRIPT */) {
|
|
3560
|
+
throw new Error(
|
|
3561
|
+
`ConsoleRemovalService only supports JAVASCRIPT, received: ${language}`
|
|
3562
|
+
);
|
|
3563
|
+
}
|
|
3564
|
+
try {
|
|
3565
|
+
const tree = await this.jsParser.parseRaw(sourceCode);
|
|
3566
|
+
const ast = tree.rootNode;
|
|
3567
|
+
const ranges = [];
|
|
3568
|
+
const visit = (node) => {
|
|
3569
|
+
if (node.type === "expression_statement") {
|
|
3570
|
+
const expr = node.firstChild;
|
|
3571
|
+
if (expr?.type === "call_expression") {
|
|
3572
|
+
const callee = expr.child(0);
|
|
3573
|
+
if (callee?.text.startsWith("console.")) {
|
|
3574
|
+
ranges.push([node.startIndex, node.endIndex]);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
for (let i = 0; i < node.childCount; i++) {
|
|
3579
|
+
visit(node.child(i));
|
|
3580
|
+
}
|
|
3581
|
+
};
|
|
3582
|
+
visit(ast);
|
|
3583
|
+
ranges.sort((a, b) => b[0] - a[0]);
|
|
3584
|
+
let cleaned = sourceCode;
|
|
3585
|
+
for (const [start, end] of ranges) {
|
|
3586
|
+
cleaned = cleaned.slice(0, start) + cleaned.slice(end);
|
|
3587
|
+
}
|
|
3588
|
+
return cleaned;
|
|
3589
|
+
} catch (error) {
|
|
3590
|
+
throw new Error(`Can not parse JS CODE ${error}`);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
3593
|
+
};
|
|
3594
|
+
|
|
3595
|
+
// packages/linter-ast/src/application/LinterAstAdapter.ts
|
|
3596
|
+
var LinterAstAdapter = class {
|
|
3597
|
+
constructor() {
|
|
3598
|
+
this.registry = new ParserRegistry();
|
|
3599
|
+
this.consoleRemovalService = new ConsoleLogRemovalService();
|
|
3600
|
+
}
|
|
3601
|
+
async parseSourceCode(sourceCode, language) {
|
|
3602
|
+
const languageKey = this.mapLanguageToParserKey(language);
|
|
3603
|
+
if (!languageKey) {
|
|
3604
|
+
throw new ParserNotAvailableError(language);
|
|
3605
|
+
}
|
|
3606
|
+
const parser = await this.registry.getParser(languageKey);
|
|
3607
|
+
return parser.parse(sourceCode);
|
|
3608
|
+
}
|
|
3609
|
+
isLanguageSupported(language) {
|
|
3610
|
+
const languageKey = this.mapLanguageToParserKey(language);
|
|
3611
|
+
return languageKey !== null;
|
|
3612
|
+
}
|
|
3613
|
+
getAvailableLanguages() {
|
|
3614
|
+
return [
|
|
3615
|
+
"TYPESCRIPT" /* TYPESCRIPT */,
|
|
3616
|
+
"TYPESCRIPT_TSX" /* TYPESCRIPT_TSX */,
|
|
3617
|
+
"JAVASCRIPT" /* JAVASCRIPT */,
|
|
3618
|
+
"JAVASCRIPT_JSX" /* JAVASCRIPT_JSX */,
|
|
3619
|
+
"CPP" /* CPP */,
|
|
3620
|
+
"GO" /* GO */,
|
|
3621
|
+
"KOTLIN" /* KOTLIN */,
|
|
3622
|
+
"CSS" /* CSS */,
|
|
3623
|
+
"CSHARP" /* CSHARP */,
|
|
3624
|
+
"PHP" /* PHP */,
|
|
3625
|
+
"PYTHON" /* PYTHON */,
|
|
3626
|
+
"RUBY" /* RUBY */,
|
|
3627
|
+
"JSON" /* JSON */,
|
|
3628
|
+
"HTML" /* HTML */,
|
|
3629
|
+
"JAVA" /* JAVA */,
|
|
3630
|
+
"SWIFT" /* SWIFT */,
|
|
3631
|
+
"SCSS" /* SCSS */,
|
|
3632
|
+
"YAML" /* YAML */
|
|
3633
|
+
];
|
|
3634
|
+
}
|
|
3635
|
+
async removeConsoleStatements(sourceCode, language) {
|
|
3636
|
+
return this.consoleRemovalService.removeConsoleLogStatements(
|
|
3637
|
+
sourceCode,
|
|
3638
|
+
language
|
|
3639
|
+
);
|
|
3640
|
+
}
|
|
3641
|
+
mapLanguageToParserKey(language) {
|
|
3642
|
+
const mapping = {
|
|
3643
|
+
["TYPESCRIPT" /* TYPESCRIPT */]: "typescript",
|
|
3644
|
+
["TYPESCRIPT_TSX" /* TYPESCRIPT_TSX */]: "typescript",
|
|
3645
|
+
["JAVASCRIPT" /* JAVASCRIPT */]: "javascript",
|
|
3646
|
+
["JAVASCRIPT_JSX" /* JAVASCRIPT_JSX */]: "javascript",
|
|
3647
|
+
["CPP" /* CPP */]: "cpp",
|
|
3648
|
+
["GO" /* GO */]: "go",
|
|
3649
|
+
["KOTLIN" /* KOTLIN */]: "kotlin",
|
|
3650
|
+
["CSS" /* CSS */]: "css",
|
|
3651
|
+
["CSHARP" /* CSHARP */]: "csharp",
|
|
3652
|
+
["PHP" /* PHP */]: "php",
|
|
3653
|
+
["PYTHON" /* PYTHON */]: "python",
|
|
3654
|
+
["RUBY" /* RUBY */]: "ruby",
|
|
3655
|
+
["JSON" /* JSON */]: "json",
|
|
3656
|
+
["HTML" /* HTML */]: "html",
|
|
3657
|
+
["JAVA" /* JAVA */]: "java",
|
|
3658
|
+
["SWIFT" /* SWIFT */]: "swift",
|
|
3659
|
+
["SCSS" /* SCSS */]: "scss",
|
|
3660
|
+
["YAML" /* YAML */]: "yaml"
|
|
3661
|
+
};
|
|
3662
|
+
return mapping[language] || null;
|
|
3663
|
+
}
|
|
3664
|
+
};
|
|
3665
|
+
|
|
3666
|
+
// packages/linter-ast/src/parsers/TypeScriptTSXParser.ts
|
|
3667
|
+
var TreeSitter17 = __toESM(require("web-tree-sitter"));
|
|
3668
|
+
|
|
3669
|
+
// packages/linter-execution/src/application/useCases/ExecuteLinterProgramsUseCase.ts
|
|
3670
|
+
var origin8 = "ExecuteLinterProgramsUseCase";
|
|
3671
|
+
var ExecuteLinterProgramsUseCase = class {
|
|
3672
|
+
constructor(linterAstAdapter = new LinterAstAdapter(), logger = new PackmindLogger(origin8)) {
|
|
3673
|
+
this.linterAstAdapter = linterAstAdapter;
|
|
3674
|
+
this.logger = logger;
|
|
3675
|
+
}
|
|
3676
|
+
async execute(command2) {
|
|
3677
|
+
const { filePath, fileContent, language, programs } = command2;
|
|
3678
|
+
if (programs.length === 0) {
|
|
3679
|
+
return {
|
|
3680
|
+
file: filePath,
|
|
3681
|
+
violations: []
|
|
3682
|
+
};
|
|
3683
|
+
}
|
|
3684
|
+
const matchingPrograms = this.filterProgramsByLanguage(programs, language);
|
|
3685
|
+
if (matchingPrograms.length === 0) {
|
|
3686
|
+
return {
|
|
3687
|
+
file: filePath,
|
|
3688
|
+
violations: []
|
|
3689
|
+
};
|
|
3690
|
+
}
|
|
3691
|
+
const violations = [];
|
|
3692
|
+
this.extractAndExecutePrograms(matchingPrograms, fileContent, violations);
|
|
3693
|
+
await this.extractAndExecuteASTPrograms(
|
|
3694
|
+
matchingPrograms,
|
|
3695
|
+
language,
|
|
3696
|
+
filePath,
|
|
3697
|
+
fileContent,
|
|
3698
|
+
violations
|
|
3699
|
+
);
|
|
3700
|
+
return {
|
|
3701
|
+
file: filePath,
|
|
3702
|
+
violations
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
async extractAndExecuteASTPrograms(programs, language, filePath, fileContent, violations) {
|
|
3706
|
+
const astPrograms = programs.filter((p) => p.sourceCodeState === "AST");
|
|
3707
|
+
if (astPrograms.length > 0) {
|
|
3708
|
+
if (!this.linterAstAdapter.isLanguageSupported(language)) {
|
|
3709
|
+
this.logger.warn("Unsupported language for AST execution", {
|
|
3710
|
+
language,
|
|
3711
|
+
filePath
|
|
3712
|
+
});
|
|
3713
|
+
} else {
|
|
3714
|
+
let ast;
|
|
3715
|
+
try {
|
|
3716
|
+
ast = await this.linterAstAdapter.parseSourceCode(
|
|
3717
|
+
fileContent,
|
|
3718
|
+
language
|
|
3719
|
+
);
|
|
3720
|
+
const astViolations = astPrograms.flatMap(
|
|
3721
|
+
(program) => this.executeAstProgram(program, ast)
|
|
3722
|
+
);
|
|
3723
|
+
violations.push(...astViolations);
|
|
3724
|
+
} catch (error) {
|
|
3725
|
+
this.logger.error("Failed to parse source code for AST execution", {
|
|
3726
|
+
language,
|
|
3727
|
+
filePath,
|
|
3728
|
+
error: this.normalizeError(error)
|
|
3729
|
+
});
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3734
|
+
extractAndExecutePrograms(programs, fileContent, violations) {
|
|
3735
|
+
const rawPrograms = programs.filter((p) => p.sourceCodeState === "RAW");
|
|
3736
|
+
if (rawPrograms.length > 0) {
|
|
3737
|
+
const rawViolations = rawPrograms.flatMap(
|
|
3738
|
+
(program) => this.executeRawProgram(program, fileContent)
|
|
3739
|
+
);
|
|
3740
|
+
violations.push(...rawViolations);
|
|
3741
|
+
}
|
|
3742
|
+
}
|
|
3743
|
+
executeRawProgram(program, fileContent) {
|
|
3744
|
+
const ruleName = this.extractRuleName(program.ruleContent);
|
|
3745
|
+
try {
|
|
3746
|
+
const checkSourceCode = this.createProgramFunction(program.code);
|
|
3747
|
+
const rawResult = checkSourceCode(fileContent);
|
|
3748
|
+
return this.mapProgramResult(rawResult, ruleName, program);
|
|
3749
|
+
} catch (error) {
|
|
3750
|
+
this.logger.error("Failed to execute detection program", {
|
|
3751
|
+
standardSlug: program.standardSlug,
|
|
3752
|
+
ruleContent: program.ruleContent,
|
|
3753
|
+
error: this.normalizeError(error)
|
|
3754
|
+
});
|
|
3755
|
+
return [];
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
executeAstProgram(program, ast) {
|
|
3759
|
+
const ruleName = this.extractRuleName(program.ruleContent);
|
|
3760
|
+
try {
|
|
3761
|
+
const checkSourceCode = this.createProgramFunction(program.code);
|
|
3762
|
+
const rawResult = checkSourceCode(ast);
|
|
3763
|
+
return this.mapProgramResult(rawResult, ruleName, program);
|
|
3764
|
+
} catch (error) {
|
|
3765
|
+
this.logger.error("Failed to execute detection program", {
|
|
3766
|
+
standardSlug: program.standardSlug,
|
|
3767
|
+
ruleContent: program.ruleContent,
|
|
3768
|
+
error: this.normalizeError(error)
|
|
3769
|
+
});
|
|
3770
|
+
return [];
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
createProgramFunction(program) {
|
|
3774
|
+
try {
|
|
3775
|
+
const func = new Function(
|
|
3776
|
+
"input",
|
|
3777
|
+
`
|
|
3778
|
+
${program}
|
|
3779
|
+
return checkSourceCode(input);
|
|
3780
|
+
`
|
|
3781
|
+
);
|
|
3782
|
+
return func;
|
|
3783
|
+
} catch (error) {
|
|
3784
|
+
throw new Error(`Failed to parse program: ${this.normalizeError(error)}`);
|
|
3785
|
+
}
|
|
3786
|
+
}
|
|
3787
|
+
mapProgramResult(rawResult, ruleName, program) {
|
|
3788
|
+
if (!Array.isArray(rawResult)) {
|
|
3789
|
+
this.logger.warn("Program result is not an array", {
|
|
3790
|
+
standardSlug: program.standardSlug,
|
|
3791
|
+
ruleContent: program.ruleContent
|
|
3792
|
+
});
|
|
3793
|
+
return [];
|
|
3794
|
+
}
|
|
3795
|
+
return rawResult.map((value) => this.toViolation(value, ruleName, program)).filter(
|
|
3796
|
+
(violation) => violation !== null
|
|
3797
|
+
);
|
|
3798
|
+
}
|
|
3799
|
+
toViolation(value, ruleName, program) {
|
|
3800
|
+
let line;
|
|
3801
|
+
let character = 0;
|
|
3802
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
3803
|
+
line = value;
|
|
3804
|
+
} else if (this.isViolationLike(value)) {
|
|
3805
|
+
line = value.line;
|
|
3806
|
+
character = value.character ?? 0;
|
|
3807
|
+
}
|
|
3808
|
+
if (!this.isValidLine(line)) {
|
|
3809
|
+
this.logger.warn("Invalid violation line detected", {
|
|
3810
|
+
standardSlug: program.standardSlug,
|
|
3811
|
+
ruleContent: program.ruleContent,
|
|
3812
|
+
line
|
|
3813
|
+
});
|
|
3814
|
+
return null;
|
|
3815
|
+
}
|
|
3816
|
+
return {
|
|
3817
|
+
line,
|
|
3818
|
+
character,
|
|
3819
|
+
rule: ruleName,
|
|
3820
|
+
standard: program.standardSlug
|
|
3821
|
+
};
|
|
3822
|
+
}
|
|
3823
|
+
isViolationLike(value) {
|
|
3824
|
+
return typeof value === "object" && value !== null && "line" in value && typeof value.line === "number";
|
|
3825
|
+
}
|
|
3826
|
+
isValidLine(line) {
|
|
3827
|
+
return typeof line === "number" && Number.isInteger(line) && line >= 0;
|
|
3828
|
+
}
|
|
3829
|
+
extractRuleName(ruleContent) {
|
|
3830
|
+
if (!ruleContent.includes("/")) {
|
|
3831
|
+
return ruleContent;
|
|
3832
|
+
}
|
|
3833
|
+
return ruleContent.split("/").pop()?.replace(".js", "") ?? ruleContent;
|
|
3834
|
+
}
|
|
3835
|
+
filterProgramsByLanguage(programs, fileLanguage) {
|
|
3836
|
+
const filtered = programs.filter((p) => p.language === fileLanguage);
|
|
3837
|
+
if (filtered.length < programs.length) {
|
|
3838
|
+
this.logger.debug("Filtered out programs with mismatched languages", {
|
|
3839
|
+
totalPrograms: programs.length,
|
|
3840
|
+
matchingPrograms: filtered.length,
|
|
3841
|
+
fileLanguage
|
|
3842
|
+
});
|
|
3843
|
+
}
|
|
3844
|
+
return filtered;
|
|
3845
|
+
}
|
|
3846
|
+
normalizeError(error) {
|
|
3847
|
+
if (error instanceof Error) {
|
|
3848
|
+
return error.message;
|
|
3849
|
+
}
|
|
3850
|
+
if (typeof error === "string") {
|
|
3851
|
+
return error;
|
|
3852
|
+
}
|
|
3853
|
+
if (error && typeof error === "object") {
|
|
3854
|
+
if ("message" in error && typeof error.message === "string") {
|
|
3855
|
+
return error.message;
|
|
3856
|
+
}
|
|
3857
|
+
try {
|
|
3858
|
+
return JSON.stringify(error);
|
|
3859
|
+
} catch {
|
|
3860
|
+
return "[object Object]";
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3863
|
+
return String(error);
|
|
3864
|
+
}
|
|
3865
|
+
};
|
|
3866
|
+
|
|
3867
|
+
// apps/cli/src/PackmindCliHexaFactory.ts
|
|
3868
|
+
var PackmindCliHexaFactory = class {
|
|
3869
|
+
constructor(logger) {
|
|
3870
|
+
this.logger = logger;
|
|
3871
|
+
this.repositories = {
|
|
3872
|
+
packmindGateway: new PackmindGateway(
|
|
3873
|
+
process.env.PACKMIND_API_KEY_V3 || ""
|
|
3874
|
+
)
|
|
3875
|
+
};
|
|
3876
|
+
this.services = {
|
|
3877
|
+
listFiles: new ListFiles(),
|
|
3878
|
+
gitRemoteUrlService: new GitService(this.logger),
|
|
3879
|
+
linterExecutionUseCase: new ExecuteLinterProgramsUseCase()
|
|
3880
|
+
};
|
|
3881
|
+
this.useCases = {
|
|
3882
|
+
executeSingleFileAst: new ExecuteSingleFileAstUseCase(
|
|
3883
|
+
this.services.linterExecutionUseCase
|
|
3884
|
+
),
|
|
3885
|
+
getGitRemoteUrl: new GetGitRemoteUrlUseCase(),
|
|
3886
|
+
listFilesInDirectoryUseCase: new ListFilesInDirectoryUseCase(),
|
|
3887
|
+
lintFilesInDirectory: new LintFilesInDirectoryUseCase(
|
|
3888
|
+
this.services,
|
|
3889
|
+
this.repositories,
|
|
3890
|
+
this.logger
|
|
3891
|
+
)
|
|
3892
|
+
};
|
|
3893
|
+
}
|
|
3894
|
+
};
|
|
3895
|
+
|
|
3896
|
+
// apps/cli/src/PackmindCliHexa.ts
|
|
3897
|
+
var origin9 = "PackmindCliHexa";
|
|
3898
|
+
var PackmindCliHexa = class {
|
|
3899
|
+
constructor(logger = new PackmindLogger(origin9)) {
|
|
3900
|
+
this.logger = logger;
|
|
3901
|
+
try {
|
|
3902
|
+
this.hexa = new PackmindCliHexaFactory(this.logger);
|
|
3903
|
+
} catch (error) {
|
|
3904
|
+
this.logger.error("Failed to initialize PackmindCliHexa", {
|
|
3905
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3906
|
+
});
|
|
3907
|
+
throw error;
|
|
3908
|
+
}
|
|
3909
|
+
}
|
|
3910
|
+
/**
|
|
3911
|
+
* Destroys the DeploymentsHexa and cleans up resources
|
|
3912
|
+
*/
|
|
3913
|
+
destroy() {
|
|
3914
|
+
this.logger.info("Destroying PackmindCliHexa");
|
|
3915
|
+
this.logger.info("PackmindCliHexa destroyed");
|
|
3916
|
+
}
|
|
3917
|
+
async getGitRemoteUrl(command2) {
|
|
3918
|
+
return this.hexa.useCases.getGitRemoteUrl.execute(command2);
|
|
3919
|
+
}
|
|
3920
|
+
async executeSingleFileAst(command2) {
|
|
3921
|
+
return this.hexa.useCases.executeSingleFileAst.execute(command2);
|
|
3922
|
+
}
|
|
3923
|
+
async listFilesInDirectory(command2) {
|
|
3924
|
+
return this.hexa.useCases.listFilesInDirectoryUseCase.execute(command2);
|
|
3925
|
+
}
|
|
3926
|
+
async lintFilesInDirectory(command2) {
|
|
3927
|
+
return this.hexa.useCases.lintFilesInDirectory.execute(command2);
|
|
3928
|
+
}
|
|
3929
|
+
};
|
|
3930
|
+
|
|
3931
|
+
// apps/cli/src/infra/repositories/IDELintLogger.ts
|
|
3932
|
+
var IDELintLogger = class {
|
|
3933
|
+
logViolations(violations) {
|
|
3934
|
+
violations.forEach((violation) => {
|
|
3935
|
+
this.logViolation(violation);
|
|
3936
|
+
});
|
|
3937
|
+
}
|
|
3938
|
+
logViolation(violation) {
|
|
3939
|
+
violation.violations.forEach(({ line, character, standard, rule }) => {
|
|
3940
|
+
console.log(
|
|
3941
|
+
`${violation.file}:${line}:${character}:error:@${standard}/${rule}`
|
|
3942
|
+
);
|
|
3943
|
+
});
|
|
3944
|
+
}
|
|
3945
|
+
};
|
|
3946
|
+
|
|
3947
|
+
// apps/cli/src/infra/repositories/HumanReadableLogger.ts
|
|
3948
|
+
var import_chalk = __toESM(require("chalk"));
|
|
3949
|
+
var HumanReadableLogger = class {
|
|
3950
|
+
logViolations(violations) {
|
|
3951
|
+
violations.forEach((violation) => {
|
|
3952
|
+
this.logViolation(violation);
|
|
3953
|
+
});
|
|
3954
|
+
if (violations.length > 0) {
|
|
3955
|
+
const totalViolationCount = violations.reduce(
|
|
3956
|
+
(acc, violation) => acc + violation.violations.length,
|
|
3957
|
+
0
|
|
3958
|
+
);
|
|
3959
|
+
console.log(
|
|
3960
|
+
import_chalk.default.bgRed.bold("packmind-cli"),
|
|
3961
|
+
import_chalk.default.red(
|
|
3962
|
+
`\u274C Found ${import_chalk.default.bold(totalViolationCount)} violation(s) in ${import_chalk.default.bold(violations.length)} file(s)`
|
|
3963
|
+
)
|
|
3964
|
+
);
|
|
3965
|
+
} else {
|
|
3966
|
+
console.log(
|
|
3967
|
+
import_chalk.default.bgGreen.bold("packmind-cli"),
|
|
3968
|
+
import_chalk.default.green.bold(`\u2705 No violations found`)
|
|
3969
|
+
);
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
logViolation(violation) {
|
|
3973
|
+
console.log(import_chalk.default.underline.gray(violation.file));
|
|
3974
|
+
violation.violations.forEach(({ line, character, standard, rule }) => {
|
|
3975
|
+
console.log(
|
|
3976
|
+
import_chalk.default.red(` ${line}:${character} error @${standard}/${rule}`)
|
|
3977
|
+
);
|
|
3978
|
+
});
|
|
3979
|
+
}
|
|
3980
|
+
};
|
|
3981
|
+
|
|
3982
|
+
// apps/cli/src/infra/commands/LinterCommand.ts
|
|
3983
|
+
var Logger = {
|
|
3984
|
+
from: async (input) => {
|
|
3985
|
+
switch (input) {
|
|
3986
|
+
case "ide":
|
|
3987
|
+
return "ide" /* ide */;
|
|
3988
|
+
case "human":
|
|
3989
|
+
return "human" /* human */;
|
|
3990
|
+
}
|
|
3991
|
+
throw new Error(
|
|
3992
|
+
`${input} is not a valid value for the --logger option. Expected values are: ide, human`
|
|
3993
|
+
);
|
|
3994
|
+
}
|
|
3995
|
+
};
|
|
3996
|
+
var RuleID = {
|
|
3997
|
+
from: async (input) => {
|
|
3998
|
+
const match = input.match(/^@([^/]+)\/(.+)$/);
|
|
3999
|
+
if (!match) {
|
|
4000
|
+
throw new Error(
|
|
4001
|
+
"Error: Invalid --rule format. Expected format: @standard-slug/ruleId"
|
|
4002
|
+
);
|
|
4003
|
+
}
|
|
4004
|
+
return {
|
|
4005
|
+
standardSlug: match[1],
|
|
4006
|
+
ruleId: createRuleId(match[2])
|
|
4007
|
+
};
|
|
4008
|
+
}
|
|
4009
|
+
};
|
|
4010
|
+
var lintCommand = (0, import_cmd_ts.command)({
|
|
4011
|
+
name: "lint",
|
|
4012
|
+
description: "Lint code at the specified path",
|
|
4013
|
+
args: {
|
|
4014
|
+
path: (0, import_cmd_ts.positional)({
|
|
4015
|
+
displayName: "path",
|
|
4016
|
+
description: "Path to lint (e.g., . for current directory)",
|
|
4017
|
+
type: import_cmd_ts.string
|
|
4018
|
+
}),
|
|
4019
|
+
logger: (0, import_cmd_ts.option)({
|
|
4020
|
+
long: "logger",
|
|
4021
|
+
description: "Output format (ide | human). Default is human",
|
|
4022
|
+
type: Logger,
|
|
4023
|
+
defaultValue: () => "human" /* human */,
|
|
4024
|
+
defaultValueIsSerializable: true
|
|
4025
|
+
}),
|
|
4026
|
+
rule: (0, import_cmd_ts.option)({
|
|
4027
|
+
long: "rule",
|
|
4028
|
+
description: "Specify rule in format @standard-slug/ruleId (runs active programs without --draft)",
|
|
4029
|
+
type: (0, import_cmd_ts.optional)(RuleID)
|
|
4030
|
+
}),
|
|
4031
|
+
language: (0, import_cmd_ts.option)({
|
|
4032
|
+
long: "language",
|
|
4033
|
+
description: "Filter detection programs by language",
|
|
4034
|
+
type: (0, import_cmd_ts.optional)(import_cmd_ts.string)
|
|
4035
|
+
}),
|
|
4036
|
+
draft: (0, import_cmd_ts.flag)({
|
|
4037
|
+
long: "draft",
|
|
4038
|
+
description: "Use draft detection programs (requires --rule)"
|
|
4039
|
+
}),
|
|
4040
|
+
debug: (0, import_cmd_ts.flag)({
|
|
4041
|
+
long: "debug",
|
|
4042
|
+
description: "Enable debug logging"
|
|
4043
|
+
})
|
|
4044
|
+
},
|
|
4045
|
+
handler: async ({ path: path2, draft, rule, debug, language, logger }) => {
|
|
4046
|
+
if (draft && !rule) {
|
|
4047
|
+
throw new Error("option --rule is required to use --draft mode");
|
|
4048
|
+
}
|
|
4049
|
+
const startedAt = Date.now();
|
|
4050
|
+
const packmindLogger = new PackmindLogger(
|
|
4051
|
+
"PackmindCLI",
|
|
4052
|
+
debug ? "debug" /* DEBUG */ : "info" /* INFO */
|
|
4053
|
+
);
|
|
4054
|
+
const packmindCliHexa = new PackmindCliHexa(packmindLogger);
|
|
4055
|
+
const { violations } = await packmindCliHexa.lintFilesInDirectory({
|
|
4056
|
+
path: path2,
|
|
4057
|
+
draftMode: draft,
|
|
4058
|
+
standardSlug: rule?.standardSlug,
|
|
4059
|
+
ruleId: rule?.ruleId,
|
|
4060
|
+
language
|
|
4061
|
+
});
|
|
4062
|
+
(logger === "ide" /* ide */ ? new IDELintLogger() : new HumanReadableLogger()).logViolations(violations);
|
|
4063
|
+
const durationSeconds = (Date.now() - startedAt) / 1e3;
|
|
4064
|
+
console.log(`Lint completed in ${durationSeconds.toFixed(2)}s`);
|
|
4065
|
+
if (violations.length > 0) {
|
|
4066
|
+
process.exit(1);
|
|
4067
|
+
} else {
|
|
4068
|
+
process.exit(0);
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
});
|
|
4072
|
+
|
|
4073
|
+
// apps/cli/src/main.ts
|
|
4074
|
+
var app = (0, import_cmd_ts2.subcommands)({
|
|
4075
|
+
name: "packmind-cli",
|
|
4076
|
+
description: "Packmind CLI tool",
|
|
4077
|
+
cmds: {
|
|
4078
|
+
lint: lintCommand
|
|
4079
|
+
}
|
|
4080
|
+
});
|
|
4081
|
+
(0, import_cmd_ts2.run)(app, process.argv.slice(2)).catch((error) => {
|
|
4082
|
+
console.error(import_chalk2.default.bgRed.bold("packmind-cli"), import_chalk2.default.red(error.message));
|
|
4083
|
+
process.exit(1);
|
|
4084
|
+
});
|