kaven-cli 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +154 -215
  2. package/dist/EnvManager-NMS3NMIE.js +15 -0
  3. package/dist/MarketplaceClient-YCFH2VU4.js +1 -0
  4. package/dist/chunk-JHLQ46NG.js +1 -0
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.js +216 -286
  7. package/dist/tier-table-DQMPQSI2.js +6 -0
  8. package/package.json +26 -10
  9. package/dist/commands/auth/login.js +0 -122
  10. package/dist/commands/auth/logout.js +0 -23
  11. package/dist/commands/auth/whoami.js +0 -36
  12. package/dist/commands/cache/index.js +0 -43
  13. package/dist/commands/config/features.js +0 -1026
  14. package/dist/commands/config/index.js +0 -95
  15. package/dist/commands/index.js +0 -2
  16. package/dist/commands/init/index.js +0 -197
  17. package/dist/commands/init-ci/index.js +0 -153
  18. package/dist/commands/license/index.js +0 -10
  19. package/dist/commands/license/status.js +0 -44
  20. package/dist/commands/license/tier-table.js +0 -46
  21. package/dist/commands/marketplace/browse.js +0 -186
  22. package/dist/commands/marketplace/install.js +0 -263
  23. package/dist/commands/marketplace/list.js +0 -122
  24. package/dist/commands/module/activate.js +0 -206
  25. package/dist/commands/module/add.js +0 -69
  26. package/dist/commands/module/doctor.js +0 -175
  27. package/dist/commands/module/publish.js +0 -258
  28. package/dist/commands/module/remove.js +0 -58
  29. package/dist/commands/telemetry/view.js +0 -27
  30. package/dist/commands/upgrade/check.js +0 -162
  31. package/dist/commands/upgrade/index.js +0 -185
  32. package/dist/core/AuthService.js +0 -222
  33. package/dist/core/CacheManager.js +0 -154
  34. package/dist/core/ConfigManager.js +0 -166
  35. package/dist/core/EnvManager.js +0 -196
  36. package/dist/core/ErrorRecovery.js +0 -192
  37. package/dist/core/LicenseService.js +0 -83
  38. package/dist/core/ManifestParser.js +0 -52
  39. package/dist/core/MarkerService.js +0 -62
  40. package/dist/core/ModuleDoctor.js +0 -451
  41. package/dist/core/ModuleInstaller.js +0 -169
  42. package/dist/core/ProjectInitializer.js +0 -166
  43. package/dist/core/RegistryResolver.js +0 -95
  44. package/dist/core/SchemaActivator.js +0 -270
  45. package/dist/core/ScriptRunner.js +0 -73
  46. package/dist/core/SignatureVerifier.js +0 -75
  47. package/dist/core/index.js +0 -2
  48. package/dist/infrastructure/Container.js +0 -37
  49. package/dist/infrastructure/MarketplaceClient.js +0 -399
  50. package/dist/infrastructure/TelemetryBuffer.js +0 -73
  51. package/dist/infrastructure/TransactionalFileSystem.js +0 -77
  52. package/dist/infrastructure/errors.js +0 -63
  53. package/dist/infrastructure/index.js +0 -2
  54. package/dist/types/auth.js +0 -2
  55. package/dist/types/manifest.js +0 -45
  56. package/dist/types/markers.js +0 -10
  57. package/dist/types/marketplace.js +0 -2
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MarkerService = void 0;
4
- const markers_1 = require("../types/markers");
5
- class MarkerService {
6
- hasModule(fileContent, moduleName) {
7
- const marker = (0, markers_1.createMarker)(moduleName);
8
- return (fileContent.includes(marker.beginMarker) &&
9
- fileContent.includes(marker.endMarker));
10
- }
11
- detectMarkers(fileContent, moduleName) {
12
- const marker = (0, markers_1.createMarker)(moduleName);
13
- const lines = fileContent.split("\n");
14
- let beginLine;
15
- let endLine;
16
- for (let i = 0; i < lines.length; i++) {
17
- if (lines[i].includes(marker.beginMarker)) {
18
- beginLine = i;
19
- }
20
- if (lines[i].includes(marker.endMarker)) {
21
- endLine = i;
22
- break;
23
- }
24
- }
25
- if (beginLine !== undefined && endLine !== undefined) {
26
- const content = lines.slice(beginLine + 1, endLine).join("\n");
27
- return {
28
- found: true,
29
- beginLine,
30
- endLine,
31
- content,
32
- };
33
- }
34
- return { found: false };
35
- }
36
- injectModule(fileContent, anchor, moduleName, code) {
37
- if (this.hasModule(fileContent, moduleName)) {
38
- throw new Error(`Module ${moduleName} already injected`);
39
- }
40
- if (!fileContent.includes(anchor)) {
41
- throw new Error(`Anchor not found: ${anchor}`);
42
- }
43
- const marker = (0, markers_1.createMarker)(moduleName);
44
- const markedCode = `\n${marker.beginMarker}\n${code}\n${marker.endMarker}\n`;
45
- return fileContent.replace(anchor, `${anchor}${markedCode}`);
46
- }
47
- removeModule(fileContent, moduleName) {
48
- const marker = (0, markers_1.createMarker)(moduleName);
49
- const beginMarker = this.escapeRegex(marker.beginMarker);
50
- const endMarker = this.escapeRegex(marker.endMarker);
51
- const regex = new RegExp(`\\n?${beginMarker}[\\s\\S]*?${endMarker}\\n?`, "g");
52
- const result = fileContent.replace(regex, "");
53
- if (result === fileContent) {
54
- throw new Error(`Module ${moduleName} not found in file`);
55
- }
56
- return result;
57
- }
58
- escapeRegex(str) {
59
- return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
60
- }
61
- }
62
- exports.MarkerService = MarkerService;
@@ -1,451 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ModuleDoctor = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const path_1 = __importDefault(require("path"));
9
- const os_1 = __importDefault(require("os"));
10
- class ModuleDoctor {
11
- projectRoot;
12
- markerService;
13
- manifestParser;
14
- constructor(projectRoot, markerService, manifestParser) {
15
- this.projectRoot = projectRoot;
16
- this.markerService = markerService;
17
- this.manifestParser = manifestParser;
18
- }
19
- async checkAll() {
20
- const results = [];
21
- results.push(...(await this.checkAnchors()));
22
- results.push(...(await this.checkMarkers()));
23
- results.push(...(await this.checkDependencies()));
24
- results.push(...(await this.checkSchemaMerge()));
25
- results.push(...(await this.checkEnvCompleteness()));
26
- results.push(...(await this.checkLicense()));
27
- results.push(...(await this.checkFrameworkVersion()));
28
- results.push(...(await this.checkPrismaClientSync()));
29
- return results;
30
- }
31
- async checkAnchors() {
32
- const results = [];
33
- const expectedAnchors = [
34
- { file: "apps/api/src/app.ts", anchor: "// [KAVEN_MODULE_IMPORTS]" },
35
- { file: "apps/api/src/app.ts", anchor: "// [KAVEN_MODULE_HOOKS]" },
36
- { file: "apps/api/src/app.ts", anchor: "// [KAVEN_MODULE_REGISTRATION]" },
37
- ];
38
- for (const { file, anchor } of expectedAnchors) {
39
- const filePath = path_1.default.join(this.projectRoot, file);
40
- if (!(await fs_extra_1.default.pathExists(filePath))) {
41
- results.push({
42
- type: "anchor",
43
- severity: "warning",
44
- message: `File not found: ${file}`,
45
- file,
46
- fixable: false,
47
- });
48
- continue;
49
- }
50
- const content = await fs_extra_1.default.readFile(filePath, "utf-8");
51
- if (!content.includes(anchor)) {
52
- results.push({
53
- type: "anchor",
54
- severity: "error",
55
- message: `Missing anchor: ${anchor}`,
56
- file,
57
- fixable: false,
58
- });
59
- }
60
- }
61
- return results;
62
- }
63
- async checkMarkers() {
64
- const results = [];
65
- const config = await this.readKavenConfig();
66
- const installedModules = config.modules.filter((m) => m.installed);
67
- for (const module of installedModules) {
68
- const manifestPath = path_1.default.join(this.projectRoot, ".kaven/modules", module.name, "module.json");
69
- if (!(await fs_extra_1.default.pathExists(manifestPath))) {
70
- results.push({
71
- type: "marker",
72
- severity: "error",
73
- message: `Manifest not found for installed module: ${module.name}`,
74
- fixable: false,
75
- });
76
- continue;
77
- }
78
- try {
79
- const manifest = await this.manifestParser.parse(manifestPath);
80
- for (const injection of manifest.injections) {
81
- const filePath = path_1.default.join(this.projectRoot, injection.file);
82
- if (!(await fs_extra_1.default.pathExists(filePath))) {
83
- results.push({
84
- type: "marker",
85
- severity: "error",
86
- message: `Injection target not found: ${injection.file}`,
87
- file: injection.file,
88
- fixable: false,
89
- });
90
- continue;
91
- }
92
- const content = await fs_extra_1.default.readFile(filePath, "utf-8");
93
- const moduleName = injection.moduleName || module.name;
94
- const detection = this.markerService.detectMarkers(content, moduleName);
95
- if (!detection.found) {
96
- results.push({
97
- type: "marker",
98
- severity: "error",
99
- message: `Module ${module.name} not injected in ${injection.file}`,
100
- file: injection.file,
101
- fixable: true,
102
- });
103
- }
104
- }
105
- }
106
- catch (error) {
107
- results.push({
108
- type: "marker",
109
- severity: "error",
110
- message: `Invalid manifest for module ${module.name}: ${error instanceof Error ? error.message : String(error)}`,
111
- fixable: false,
112
- });
113
- }
114
- }
115
- return results;
116
- }
117
- async checkDependencies() {
118
- const results = [];
119
- const config = await this.readKavenConfig();
120
- const installedModules = config.modules.filter((m) => m.installed);
121
- const packageJsonPath = path_1.default.join(this.projectRoot, "package.json");
122
- if (!(await fs_extra_1.default.pathExists(packageJsonPath))) {
123
- results.push({
124
- type: "dependency",
125
- severity: "error",
126
- message: "package.json not found",
127
- fixable: false,
128
- });
129
- return results;
130
- }
131
- const packageJson = await fs_extra_1.default.readJSON(packageJsonPath);
132
- for (const module of installedModules) {
133
- const manifestPath = path_1.default.join(this.projectRoot, ".kaven/modules", module.name, "module.json");
134
- if (!(await fs_extra_1.default.pathExists(manifestPath)))
135
- continue;
136
- try {
137
- const manifest = await this.manifestParser.parse(manifestPath);
138
- for (const dep of manifest.dependencies.npm) {
139
- const [name] = dep.split("@");
140
- const hasInDeps = packageJson.dependencies?.[name];
141
- const hasInDevDeps = packageJson.devDependencies?.[name];
142
- if (!hasInDeps && !hasInDevDeps) {
143
- results.push({
144
- type: "dependency",
145
- severity: "warning",
146
- message: `Missing npm dependency: ${dep}`,
147
- fixable: true,
148
- });
149
- }
150
- }
151
- }
152
- catch {
153
- // Ignorar erros de manifest aqui, já tratados em checkMarkers
154
- }
155
- }
156
- return results;
157
- }
158
- // ──────────────────────────────────────────────────────────
159
- // C2.4: New enhanced checks
160
- // ──────────────────────────────────────────────────────────
161
- /** Check that the base Prisma schema exists and has no merge conflicts. */
162
- async checkSchemaMerge() {
163
- const results = [];
164
- const baseSchemaPath = path_1.default.join(this.projectRoot, "packages/database/prisma/schema.base.prisma");
165
- if (!(await fs_extra_1.default.pathExists(baseSchemaPath))) {
166
- results.push({
167
- type: "dependency",
168
- severity: "warning",
169
- message: "Prisma base schema not found: packages/database/prisma/schema.base.prisma",
170
- file: "packages/database/prisma/schema.base.prisma",
171
- fixable: false,
172
- });
173
- return results;
174
- }
175
- // Check for git merge conflict markers
176
- const schemaDir = path_1.default.dirname(baseSchemaPath);
177
- try {
178
- const schemaFiles = await fs_extra_1.default.readdir(schemaDir);
179
- for (const schemaFile of schemaFiles) {
180
- if (!schemaFile.endsWith(".prisma"))
181
- continue;
182
- const filePath = path_1.default.join(schemaDir, schemaFile);
183
- const content = await fs_extra_1.default.readFile(filePath, "utf-8");
184
- if (content.includes("<<<<<<<")) {
185
- results.push({
186
- type: "marker",
187
- severity: "error",
188
- message: `Merge conflict detected in schema file: ${schemaFile}`,
189
- file: path_1.default.join("packages/database/prisma", schemaFile),
190
- fixable: false,
191
- });
192
- }
193
- }
194
- }
195
- catch {
196
- // Non-critical
197
- }
198
- if (results.length === 0) {
199
- results.push({
200
- type: "dependency",
201
- severity: "info",
202
- message: "Prisma schema integrity OK",
203
- fixable: false,
204
- });
205
- }
206
- return results;
207
- }
208
- /** Check that .env has all keys defined in .env.example (non-optional). */
209
- async checkEnvCompleteness() {
210
- const results = [];
211
- const envExamplePath = path_1.default.join(this.projectRoot, ".env.example");
212
- const envPath = path_1.default.join(this.projectRoot, ".env");
213
- if (!(await fs_extra_1.default.pathExists(envExamplePath))) {
214
- results.push({
215
- type: "dependency",
216
- severity: "info",
217
- message: ".env.example not found — skipping env completeness check",
218
- fixable: false,
219
- });
220
- return results;
221
- }
222
- if (!(await fs_extra_1.default.pathExists(envPath))) {
223
- results.push({
224
- type: "dependency",
225
- severity: "warning",
226
- message: ".env file not found. Copy .env.example to .env and fill in values",
227
- file: ".env",
228
- fixable: false,
229
- });
230
- return results;
231
- }
232
- const exampleContent = await fs_extra_1.default.readFile(envExamplePath, "utf-8");
233
- const envContent = await fs_extra_1.default.readFile(envPath, "utf-8");
234
- // Parse keys: lines that are KEY=VALUE (not comments, not blank)
235
- const parseKeys = (content) => {
236
- const keys = new Set();
237
- for (const line of content.split("\n")) {
238
- const trimmed = line.trim();
239
- if (trimmed.startsWith("#") || !trimmed.includes("="))
240
- continue;
241
- const key = trimmed.split("=")[0].trim();
242
- if (key)
243
- keys.add(key);
244
- }
245
- return keys;
246
- };
247
- const exampleKeys = parseKeys(exampleContent);
248
- const envKeys = parseKeys(envContent);
249
- const missingKeys = [];
250
- for (const key of exampleKeys) {
251
- if (!envKeys.has(key)) {
252
- missingKeys.push(key);
253
- }
254
- }
255
- if (missingKeys.length > 0) {
256
- results.push({
257
- type: "dependency",
258
- severity: "warning",
259
- message: `Missing env vars in .env: ${missingKeys.join(", ")}`,
260
- file: ".env",
261
- fixable: true,
262
- });
263
- }
264
- else {
265
- results.push({
266
- type: "dependency",
267
- severity: "info",
268
- message: "Env vars completeness OK",
269
- fixable: false,
270
- });
271
- }
272
- return results;
273
- }
274
- /** Check if the stored license is present and not expired. */
275
- async checkLicense() {
276
- const results = [];
277
- const licensePath = path_1.default.join(os_1.default.homedir(), ".kaven", "license.json");
278
- if (!(await fs_extra_1.default.pathExists(licensePath))) {
279
- results.push({
280
- type: "dependency",
281
- severity: "warning",
282
- message: "No license found at ~/.kaven/license.json. Run 'kaven license status' to set up.",
283
- fixable: false,
284
- });
285
- return results;
286
- }
287
- try {
288
- const licenseData = await fs_extra_1.default.readJson(licensePath);
289
- if (licenseData.expiresAt) {
290
- const expiresAt = new Date(licenseData.expiresAt).getTime();
291
- if (Date.now() > expiresAt) {
292
- results.push({
293
- type: "dependency",
294
- severity: "error",
295
- message: `License expired on ${licenseData.expiresAt}. Run 'kaven upgrade' to renew.`,
296
- fixable: false,
297
- });
298
- return results;
299
- }
300
- }
301
- results.push({
302
- type: "dependency",
303
- severity: "info",
304
- message: `License valid (tier: ${licenseData.tier || "unknown"})`,
305
- fixable: false,
306
- });
307
- }
308
- catch {
309
- results.push({
310
- type: "dependency",
311
- severity: "warning",
312
- message: "Could not read license file. Try 'kaven license status'.",
313
- fixable: false,
314
- });
315
- }
316
- return results;
317
- }
318
- /** Check if the framework version in package.json is compatible. */
319
- async checkFrameworkVersion() {
320
- const results = [];
321
- const packageJsonPath = path_1.default.join(this.projectRoot, "package.json");
322
- if (!(await fs_extra_1.default.pathExists(packageJsonPath))) {
323
- results.push({
324
- type: "dependency",
325
- severity: "info",
326
- message: "package.json not found — skipping framework version check",
327
- fixable: false,
328
- });
329
- return results;
330
- }
331
- try {
332
- const packageJson = await fs_extra_1.default.readJSON(packageJsonPath);
333
- const kavenCoreVersion = packageJson.dependencies?.["@kaven/core"] ||
334
- packageJson.devDependencies?.["@kaven/core"];
335
- if (!kavenCoreVersion) {
336
- results.push({
337
- type: "dependency",
338
- severity: "info",
339
- message: "@kaven/core not found in dependencies — not a Kaven framework project",
340
- fixable: false,
341
- });
342
- return results;
343
- }
344
- // Minimum required semver range
345
- const MINIMUM_VERSION = "1.0.0";
346
- const versionStr = kavenCoreVersion.replace(/[\^~>=<]/, "").split(" ")[0];
347
- const parts = versionStr.split(".").map(Number);
348
- const minParts = MINIMUM_VERSION.split(".").map(Number);
349
- let compatible = true;
350
- for (let i = 0; i < 3; i++) {
351
- if ((parts[i] || 0) > (minParts[i] || 0))
352
- break;
353
- if ((parts[i] || 0) < (minParts[i] || 0)) {
354
- compatible = false;
355
- break;
356
- }
357
- }
358
- if (!compatible) {
359
- results.push({
360
- type: "dependency",
361
- severity: "warning",
362
- message: `@kaven/core version ${kavenCoreVersion} may be outdated. Minimum: ^${MINIMUM_VERSION}`,
363
- fixable: false,
364
- });
365
- }
366
- else {
367
- results.push({
368
- type: "dependency",
369
- severity: "info",
370
- message: `Framework version OK (${kavenCoreVersion})`,
371
- fixable: false,
372
- });
373
- }
374
- }
375
- catch {
376
- results.push({
377
- type: "dependency",
378
- severity: "info",
379
- message: "Could not determine framework version",
380
- fixable: false,
381
- });
382
- }
383
- return results;
384
- }
385
- /** Check if Prisma client is generated and up-to-date. */
386
- async checkPrismaClientSync() {
387
- const results = [];
388
- const prismaClientPath = path_1.default.join(this.projectRoot, "node_modules/@prisma/client");
389
- const schemaPath = path_1.default.join(this.projectRoot, "prisma/schema.prisma");
390
- if (!(await fs_extra_1.default.pathExists(prismaClientPath))) {
391
- results.push({
392
- type: "dependency",
393
- severity: "warning",
394
- message: "@prisma/client not found. Run 'npx prisma generate' to generate the client.",
395
- fixable: true,
396
- });
397
- return results;
398
- }
399
- if (!(await fs_extra_1.default.pathExists(schemaPath))) {
400
- results.push({
401
- type: "dependency",
402
- severity: "info",
403
- message: "prisma/schema.prisma not found — skipping Prisma sync check",
404
- fixable: false,
405
- });
406
- return results;
407
- }
408
- try {
409
- const schemaStat = await fs_extra_1.default.stat(schemaPath);
410
- const clientStat = await fs_extra_1.default.stat(prismaClientPath);
411
- if (schemaStat.mtime > clientStat.mtime) {
412
- results.push({
413
- type: "dependency",
414
- severity: "warning",
415
- message: "Prisma schema was modified after client generation. Run 'npx prisma generate'.",
416
- fixable: true,
417
- });
418
- }
419
- else {
420
- results.push({
421
- type: "dependency",
422
- severity: "info",
423
- message: "Prisma client is up to date",
424
- fixable: false,
425
- });
426
- }
427
- }
428
- catch {
429
- results.push({
430
- type: "dependency",
431
- severity: "info",
432
- message: "Could not compare Prisma schema and client timestamps",
433
- fixable: false,
434
- });
435
- }
436
- return results;
437
- }
438
- async readKavenConfig() {
439
- const configPath = path_1.default.join(this.projectRoot, "kaven.json");
440
- if (!(await fs_extra_1.default.pathExists(configPath))) {
441
- return { modules: [] };
442
- }
443
- try {
444
- return await fs_extra_1.default.readJSON(configPath);
445
- }
446
- catch {
447
- return { modules: [] };
448
- }
449
- }
450
- }
451
- exports.ModuleDoctor = ModuleDoctor;
@@ -1,169 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ModuleInstaller = void 0;
7
- const TransactionalFileSystem_1 = require("../infrastructure/TransactionalFileSystem");
8
- const ScriptRunner_js_1 = require("./ScriptRunner.js");
9
- const fs_extra_1 = __importDefault(require("fs-extra"));
10
- const path_1 = __importDefault(require("path"));
11
- const chalk_1 = __importDefault(require("chalk"));
12
- class ModuleInstaller {
13
- projectRoot;
14
- markerService;
15
- constructor(projectRoot, markerService) {
16
- this.projectRoot = projectRoot;
17
- this.markerService = markerService;
18
- }
19
- /**
20
- * Check whether the given module slug is already installed by scanning
21
- * project files for its begin/end markers.
22
- */
23
- async isModuleInstalled(moduleName) {
24
- try {
25
- const filesToCheck = await this.findProjectFiles();
26
- for (const filePath of filesToCheck) {
27
- try {
28
- const content = await fs_extra_1.default.readFile(filePath, "utf-8");
29
- if (this.markerService.hasModule(content, moduleName)) {
30
- return true;
31
- }
32
- }
33
- catch {
34
- // Skip unreadable files
35
- }
36
- }
37
- return false;
38
- }
39
- catch {
40
- return false;
41
- }
42
- }
43
- /** Find text-based project source files to check for markers. */
44
- async findProjectFiles() {
45
- const { glob } = await import("glob");
46
- const patterns = ["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx"];
47
- const ignore = ["**/node_modules/**", "**/.next/**", "**/dist/**", "**/build/**"];
48
- const files = [];
49
- for (const pattern of patterns) {
50
- const found = await glob(pattern, {
51
- cwd: this.projectRoot,
52
- absolute: true,
53
- ignore,
54
- });
55
- files.push(...found);
56
- }
57
- return [...new Set(files)];
58
- }
59
- async install(manifest, options) {
60
- const tx = new TransactionalFileSystem_1.TransactionalFileSystem(this.projectRoot);
61
- try {
62
- const filesToModify = Array.from(new Set(manifest.injections.map((inj) => inj.file)));
63
- await tx.backup(filesToModify);
64
- for (const injection of manifest.injections) {
65
- await this.injectCode(injection);
66
- }
67
- await tx.commit();
68
- // Run postInstall lifecycle scripts if defined in module.json
69
- const moduleJson = await this.readManifest(this.projectRoot);
70
- if (moduleJson?.scripts?.postInstall?.length) {
71
- const runner = new ScriptRunner_js_1.ScriptRunner();
72
- try {
73
- await runner.runScripts(moduleJson.scripts.postInstall.map((s) => ({ ...s, cwd: this.projectRoot })), 'postInstall', false);
74
- }
75
- catch (err) {
76
- const errMsg = err instanceof Error ? err.message : String(err);
77
- console.warn(chalk_1.default.yellow(`
78
- ⚠ PostInstall script failed: ${errMsg}`));
79
- console.warn(chalk_1.default.dim(' The module is installed. Run the script manually if needed.'));
80
- }
81
- }
82
- // Inject environment variables if defined in module.json
83
- if (!options?.skipEnv && moduleJson?.env?.length) {
84
- const { EnvManager } = await import('./EnvManager.js');
85
- const envManager = new EnvManager();
86
- const envVarDefs = moduleJson.env.map((e) => ({
87
- name: e.key,
88
- description: e.example ?? e.key,
89
- required: e.required ?? false,
90
- }));
91
- await envManager.injectEnvVars(manifest.name, envVarDefs, {
92
- projectDir: this.projectRoot,
93
- envFile: options?.envFile,
94
- skipEnv: options?.skipEnv,
95
- skipConfirmation: options?.yes,
96
- });
97
- }
98
- }
99
- catch (error) {
100
- const errorMessage = error instanceof Error ? error.message : String(error);
101
- console.error(`❌ Installation failed: ${errorMessage}`);
102
- console.log(`🔄 Rolling back...`);
103
- await tx.rollback();
104
- throw error;
105
- }
106
- }
107
- async uninstall(manifest, options) {
108
- const tx = new TransactionalFileSystem_1.TransactionalFileSystem(this.projectRoot);
109
- try {
110
- const filesToModify = Array.from(new Set(manifest.injections.map((inj) => inj.file)));
111
- await tx.backup(filesToModify);
112
- // Remove environment variables before removing files
113
- const { EnvManager } = await import('./EnvManager.js');
114
- const envManager = new EnvManager();
115
- envManager.removeEnvVars(manifest.name, {
116
- projectDir: this.projectRoot,
117
- skipEnv: options?.skipEnv,
118
- });
119
- // Run preRemove lifecycle scripts if defined in module.json
120
- const moduleJson = await this.readManifest(this.projectRoot);
121
- if (moduleJson?.scripts?.preRemove?.length) {
122
- const runner = new ScriptRunner_js_1.ScriptRunner();
123
- try {
124
- await runner.runScripts(moduleJson.scripts.preRemove.map((s) => ({ ...s, cwd: this.projectRoot })), 'preRemove', false);
125
- }
126
- catch (err) {
127
- const errMsg = err instanceof Error ? err.message : String(err);
128
- console.warn(chalk_1.default.yellow(`
129
- ⚠ PreRemove script failed: ${errMsg}`));
130
- }
131
- }
132
- // Na desinstalação, removemos por arquivo para evitar múltiplas tentativas
133
- // de remover marcadores que o regex global já removeu.
134
- for (const fileName of filesToModify) {
135
- await this.removeCode(fileName, manifest.name);
136
- }
137
- await tx.commit();
138
- }
139
- catch (error) {
140
- const errorMessage = error instanceof Error ? error.message : String(error);
141
- console.error(`❌ Removal failed: ${errorMessage}`);
142
- console.log(`🔄 Rolling back...`);
143
- await tx.rollback();
144
- throw error;
145
- }
146
- }
147
- async injectCode(injection) {
148
- const filePath = path_1.default.join(this.projectRoot, injection.file);
149
- const content = await fs_extra_1.default.readFile(filePath, "utf-8");
150
- const updated = this.markerService.injectModule(content, injection.anchor, injection.moduleName || "unnamed", injection.code);
151
- await fs_extra_1.default.writeFile(filePath, updated);
152
- }
153
- async removeCode(fileName, moduleName) {
154
- const filePath = path_1.default.join(this.projectRoot, fileName);
155
- const content = await fs_extra_1.default.readFile(filePath, "utf-8");
156
- const updated = this.markerService.removeModule(content, moduleName);
157
- await fs_extra_1.default.writeFile(filePath, updated);
158
- }
159
- async readManifest(dir) {
160
- try {
161
- const raw = await fs_extra_1.default.readFile(path_1.default.join(dir, 'module.json'), 'utf-8');
162
- return JSON.parse(raw);
163
- }
164
- catch {
165
- return null;
166
- }
167
- }
168
- }
169
- exports.ModuleInstaller = ModuleInstaller;