@juspay/yama 1.0.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.
@@ -0,0 +1,590 @@
1
+ "use strict";
2
+ /**
3
+ * Enhanced Configuration Manager for Yama
4
+ * Handles configuration loading, validation, and merging from multiple sources
5
+ */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ exports.configManager = exports.ConfigManager = void 0;
11
+ exports.createConfigManager = createConfigManager;
12
+ const fs_1 = __importDefault(require("fs"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const yaml_1 = __importDefault(require("yaml"));
15
+ const types_1 = require("../types");
16
+ const Logger_1 = require("./Logger");
17
+ class ConfigManager {
18
+ constructor() {
19
+ this.config = null;
20
+ this.configPaths = [];
21
+ this.setupConfigPaths();
22
+ }
23
+ /**
24
+ * Setup configuration file search paths
25
+ */
26
+ setupConfigPaths() {
27
+ const cwd = process.cwd();
28
+ const homeDir = require("os").homedir();
29
+ this.configPaths = [
30
+ path_1.default.join(cwd, "yama.config.yaml"),
31
+ path_1.default.join(cwd, "yama.config.yml"),
32
+ path_1.default.join(cwd, "yama.config.json"),
33
+ path_1.default.join(cwd, ".yama.yaml"),
34
+ path_1.default.join(cwd, ".yama.yml"),
35
+ path_1.default.join(cwd, ".yama.json"),
36
+ path_1.default.join(homeDir, ".yama", "config.yaml"),
37
+ path_1.default.join(homeDir, ".yama", "config.yml"),
38
+ path_1.default.join(homeDir, ".yama", "config.json"),
39
+ path_1.default.join(homeDir, ".config", "yama", "config.yaml"),
40
+ path_1.default.join(homeDir, ".config", "yama", "config.yml"),
41
+ path_1.default.join(homeDir, ".config", "yama", "config.json"),
42
+ ];
43
+ }
44
+ /**
45
+ * Load configuration from files and environment
46
+ */
47
+ async loadConfig(configPath) {
48
+ if (this.config) {
49
+ return this.config;
50
+ }
51
+ Logger_1.logger.debug("Loading Yama configuration...");
52
+ // Start with default config
53
+ let config = this.deepClone(ConfigManager.DEFAULT_CONFIG);
54
+ // If specific config path provided, use only that
55
+ if (configPath) {
56
+ if (!fs_1.default.existsSync(configPath)) {
57
+ throw new types_1.ConfigurationError(`Configuration file not found: ${configPath}`);
58
+ }
59
+ const fileConfig = await this.loadConfigFile(configPath);
60
+ config = this.mergeConfigs(config, fileConfig);
61
+ Logger_1.logger.debug(`Loaded configuration from: ${configPath}`);
62
+ }
63
+ else {
64
+ // Search for config files in predefined paths
65
+ for (const configFilePath of this.configPaths) {
66
+ if (fs_1.default.existsSync(configFilePath)) {
67
+ try {
68
+ const fileConfig = await this.loadConfigFile(configFilePath);
69
+ config = this.mergeConfigs(config, fileConfig);
70
+ Logger_1.logger.debug(`Loaded configuration from: ${configFilePath}`);
71
+ break;
72
+ }
73
+ catch (error) {
74
+ Logger_1.logger.warn(`Failed to load config from ${configFilePath}:`, error);
75
+ }
76
+ }
77
+ }
78
+ }
79
+ // Override with environment variables
80
+ config = this.applyEnvironmentOverrides(config);
81
+ // Validate configuration
82
+ this.validateConfig(config);
83
+ this.config = config;
84
+ Logger_1.logger.debug("Configuration loaded successfully");
85
+ return config;
86
+ }
87
+ /**
88
+ * Load configuration from a specific file
89
+ */
90
+ async loadConfigFile(filePath) {
91
+ try {
92
+ const content = fs_1.default.readFileSync(filePath, "utf8");
93
+ const ext = path_1.default.extname(filePath).toLowerCase();
94
+ switch (ext) {
95
+ case ".yaml":
96
+ case ".yml":
97
+ return yaml_1.default.parse(content);
98
+ case ".json":
99
+ return JSON.parse(content);
100
+ default:
101
+ throw new types_1.ConfigurationError(`Unsupported config file format: ${ext}`);
102
+ }
103
+ }
104
+ catch (error) {
105
+ throw new types_1.ConfigurationError(`Failed to parse config file ${filePath}: ${error.message}`);
106
+ }
107
+ }
108
+ /**
109
+ * Apply environment variable overrides
110
+ */
111
+ applyEnvironmentOverrides(config) {
112
+ const env = process.env;
113
+ // AI Provider overrides
114
+ if (env.AI_PROVIDER) {
115
+ config.providers.ai.provider = env.AI_PROVIDER;
116
+ }
117
+ if (env.AI_MODEL) {
118
+ config.providers.ai.model = env.AI_MODEL;
119
+ }
120
+ if (env.AI_TIMEOUT) {
121
+ config.providers.ai.timeout = env.AI_TIMEOUT;
122
+ }
123
+ if (env.AI_TEMPERATURE) {
124
+ config.providers.ai.temperature = parseFloat(env.AI_TEMPERATURE);
125
+ }
126
+ if (env.AI_MAX_TOKENS) {
127
+ config.providers.ai.maxTokens = parseInt(env.AI_MAX_TOKENS);
128
+ }
129
+ // Git Provider overrides
130
+ if (env.BITBUCKET_USERNAME) {
131
+ config.providers.git.credentials.username = env.BITBUCKET_USERNAME;
132
+ }
133
+ if (env.BITBUCKET_TOKEN) {
134
+ config.providers.git.credentials.token = env.BITBUCKET_TOKEN;
135
+ }
136
+ if (env.BITBUCKET_BASE_URL) {
137
+ config.providers.git.credentials.baseUrl = env.BITBUCKET_BASE_URL;
138
+ }
139
+ // Feature toggles
140
+ if (env.ENABLE_CODE_REVIEW !== undefined) {
141
+ config.features.codeReview.enabled = env.ENABLE_CODE_REVIEW === "true";
142
+ }
143
+ if (env.ENABLE_DESCRIPTION_ENHANCEMENT !== undefined) {
144
+ config.features.descriptionEnhancement.enabled =
145
+ env.ENABLE_DESCRIPTION_ENHANCEMENT === "true";
146
+ }
147
+ if (env.ENABLE_SECURITY_SCAN !== undefined) {
148
+ config.features.securityScan.enabled =
149
+ env.ENABLE_SECURITY_SCAN === "true";
150
+ }
151
+ if (env.ENABLE_ANALYTICS !== undefined) {
152
+ config.features.analytics.enabled = env.ENABLE_ANALYTICS === "true";
153
+ }
154
+ // Cache configuration
155
+ if (env.CACHE_ENABLED !== undefined) {
156
+ config.cache.enabled = env.CACHE_ENABLED === "true";
157
+ }
158
+ if (env.CACHE_TTL) {
159
+ config.cache.ttl = env.CACHE_TTL;
160
+ }
161
+ if (env.CACHE_STORAGE) {
162
+ config.cache.storage = env.CACHE_STORAGE;
163
+ }
164
+ // Debug mode
165
+ if (env.GUARDIAN_DEBUG === "true") {
166
+ Logger_1.logger.setLevel("debug");
167
+ Logger_1.logger.setVerbose(true);
168
+ }
169
+ Logger_1.logger.debug("Applied environment variable overrides");
170
+ return config;
171
+ }
172
+ /**
173
+ * Validate configuration
174
+ */
175
+ validateConfig(config) {
176
+ const errors = [];
177
+ // Validate AI provider credentials
178
+ if (!config.providers.ai.provider) {
179
+ errors.push("AI provider must be specified");
180
+ }
181
+ // Validate Git provider credentials
182
+ if (!config.providers.git.credentials.username) {
183
+ errors.push("Git username must be specified");
184
+ }
185
+ if (!config.providers.git.credentials.token) {
186
+ errors.push("Git token must be specified");
187
+ }
188
+ // Validate enabled features have required configuration
189
+ if (config.features.codeReview.enabled) {
190
+ if (!config.features.codeReview.severityLevels?.length) {
191
+ errors.push("Code review severity levels must be specified when enabled");
192
+ }
193
+ }
194
+ if (config.features.descriptionEnhancement.enabled) {
195
+ if (!config.features.descriptionEnhancement.requiredSections?.length) {
196
+ errors.push("Description enhancement required sections must be specified when enabled");
197
+ }
198
+ }
199
+ // Validate cache configuration
200
+ if (config.cache?.enabled) {
201
+ if (!config.cache.storage) {
202
+ errors.push("Cache storage type must be specified when cache is enabled");
203
+ }
204
+ }
205
+ if (errors.length > 0) {
206
+ throw new types_1.ConfigurationError(`Configuration validation failed:\n${errors.map((e) => ` - ${e}`).join("\n")}`);
207
+ }
208
+ Logger_1.logger.debug("Configuration validation passed");
209
+ }
210
+ /**
211
+ * Merge two configuration objects deeply
212
+ */
213
+ mergeConfigs(base, override) {
214
+ return this.deepMerge(base, override);
215
+ }
216
+ /**
217
+ * Deep merge utility
218
+ */
219
+ deepMerge(target, source) {
220
+ const result = { ...target };
221
+ for (const key in source) {
222
+ if (source[key] !== null &&
223
+ typeof source[key] === "object" &&
224
+ !Array.isArray(source[key])) {
225
+ result[key] = this.deepMerge(target[key] || {}, source[key]);
226
+ }
227
+ else {
228
+ result[key] = source[key];
229
+ }
230
+ }
231
+ return result;
232
+ }
233
+ /**
234
+ * Deep clone utility
235
+ */
236
+ deepClone(obj) {
237
+ return JSON.parse(JSON.stringify(obj));
238
+ }
239
+ /**
240
+ * Get current configuration
241
+ */
242
+ getConfig() {
243
+ if (!this.config) {
244
+ throw new types_1.ConfigurationError("Configuration not loaded. Call loadConfig() first.");
245
+ }
246
+ return this.config;
247
+ }
248
+ /**
249
+ * Create default configuration file
250
+ */
251
+ async createDefaultConfig(outputPath) {
252
+ const defaultPath = outputPath || path_1.default.join(process.cwd(), "yama.config.yaml");
253
+ const configContent = yaml_1.default.stringify(ConfigManager.DEFAULT_CONFIG, {
254
+ indent: 2,
255
+ lineWidth: 100,
256
+ });
257
+ // Add comments to make the config file more user-friendly
258
+ const commentedConfig = this.addConfigComments(configContent);
259
+ fs_1.default.writeFileSync(defaultPath, commentedConfig, "utf8");
260
+ Logger_1.logger.info(`Default configuration created at: ${defaultPath}`);
261
+ return defaultPath;
262
+ }
263
+ /**
264
+ * Add helpful comments to configuration file
265
+ */
266
+ addConfigComments(content) {
267
+ const header = `# Yama Configuration
268
+ # This file configures all aspects of Yama behavior
269
+ # For more information, visit: https://github.com/juspay/yama
270
+
271
+ `;
272
+ const sections = [
273
+ "# AI Provider Configuration",
274
+ "# Git Platform Configuration",
275
+ "# Feature Configuration",
276
+ "# Cache Configuration",
277
+ "# Performance Configuration",
278
+ "# Custom Rules Configuration",
279
+ "# Reporting Configuration",
280
+ ];
281
+ let commented = header + content;
282
+ // Add section comments (this is a simplified approach)
283
+ sections.forEach((section) => {
284
+ const key = section.split(" ")[1].toLowerCase();
285
+ commented = commented.replace(new RegExp(`^(${key}:)`, "m"), `${section}\n$1`);
286
+ });
287
+ return commented;
288
+ }
289
+ /**
290
+ * Validate specific configuration section
291
+ */
292
+ validateSection(section, config) {
293
+ try {
294
+ switch (section) {
295
+ case "providers":
296
+ return this.validateProviders(config);
297
+ case "features":
298
+ return this.validateFeatures(config);
299
+ case "cache":
300
+ return this.validateCache(config);
301
+ default:
302
+ return true;
303
+ }
304
+ }
305
+ catch (error) {
306
+ Logger_1.logger.error(`Validation failed for section ${section}:`, error);
307
+ return false;
308
+ }
309
+ }
310
+ validateProviders(providers) {
311
+ return !!(providers?.ai?.provider &&
312
+ providers?.git?.credentials?.username &&
313
+ providers?.git?.credentials?.token);
314
+ }
315
+ validateFeatures(features) {
316
+ return !!(features && typeof features === "object");
317
+ }
318
+ validateCache(cacheConfig) {
319
+ if (!cacheConfig?.enabled) {
320
+ return true;
321
+ }
322
+ return !!(cacheConfig.storage && cacheConfig.ttl);
323
+ }
324
+ /**
325
+ * Get configuration schema for validation
326
+ */
327
+ getSchema() {
328
+ return {
329
+ type: "object",
330
+ required: ["providers", "features"],
331
+ properties: {
332
+ providers: {
333
+ type: "object",
334
+ required: ["ai", "git"],
335
+ properties: {
336
+ ai: {
337
+ type: "object",
338
+ required: ["provider"],
339
+ properties: {
340
+ provider: { type: "string" },
341
+ model: { type: "string" },
342
+ enableFallback: { type: "boolean" },
343
+ enableAnalytics: { type: "boolean" },
344
+ timeout: { type: ["string", "number"] },
345
+ temperature: { type: "number", minimum: 0, maximum: 2 },
346
+ maxTokens: { type: "number", minimum: 1 },
347
+ },
348
+ },
349
+ git: {
350
+ type: "object",
351
+ required: ["platform", "credentials"],
352
+ properties: {
353
+ platform: {
354
+ type: "string",
355
+ enum: ["bitbucket", "github", "gitlab", "azure-devops"],
356
+ },
357
+ credentials: {
358
+ type: "object",
359
+ required: ["username", "token"],
360
+ properties: {
361
+ username: { type: "string" },
362
+ token: { type: "string" },
363
+ baseUrl: { type: "string" },
364
+ },
365
+ },
366
+ },
367
+ },
368
+ },
369
+ },
370
+ features: {
371
+ type: "object",
372
+ required: ["codeReview", "descriptionEnhancement"],
373
+ properties: {
374
+ codeReview: {
375
+ type: "object",
376
+ required: ["enabled"],
377
+ properties: {
378
+ enabled: { type: "boolean" },
379
+ severityLevels: { type: "array", items: { type: "string" } },
380
+ categories: { type: "array", items: { type: "string" } },
381
+ excludePatterns: { type: "array", items: { type: "string" } },
382
+ },
383
+ },
384
+ descriptionEnhancement: {
385
+ type: "object",
386
+ required: ["enabled"],
387
+ properties: {
388
+ enabled: { type: "boolean" },
389
+ preserveContent: { type: "boolean" },
390
+ requiredSections: { type: "array" },
391
+ autoFormat: { type: "boolean" },
392
+ },
393
+ },
394
+ },
395
+ },
396
+ },
397
+ };
398
+ }
399
+ /**
400
+ * Find the first available configuration file
401
+ */
402
+ findConfigFile() {
403
+ for (const configPath of this.configPaths) {
404
+ if (fs_1.default.existsSync(configPath)) {
405
+ return configPath;
406
+ }
407
+ }
408
+ return null;
409
+ }
410
+ /**
411
+ * Watch configuration file for changes and reload automatically
412
+ */
413
+ watchConfig(callback) {
414
+ if (!this.configPaths.length) {
415
+ Logger_1.logger.warn("No configuration file found to watch");
416
+ return () => { };
417
+ }
418
+ const configPath = this.findConfigFile();
419
+ if (!configPath) {
420
+ Logger_1.logger.warn("No configuration file found to watch");
421
+ return () => { };
422
+ }
423
+ Logger_1.logger.debug(`Watching configuration file: ${configPath}`);
424
+ fs_1.default.watchFile(configPath, { interval: 1000 }, async () => {
425
+ try {
426
+ Logger_1.logger.info("Configuration file changed, reloading...");
427
+ const newConfig = await this.loadConfig();
428
+ if (callback) {
429
+ callback(newConfig);
430
+ }
431
+ Logger_1.logger.success("Configuration reloaded successfully");
432
+ }
433
+ catch (error) {
434
+ Logger_1.logger.error(`Failed to reload configuration: ${error.message}`);
435
+ }
436
+ });
437
+ // Return cleanup function
438
+ return () => {
439
+ fs_1.default.unwatchFile(configPath);
440
+ Logger_1.logger.debug("Stopped watching configuration file");
441
+ };
442
+ }
443
+ /**
444
+ * Enable hot-reload for configuration
445
+ */
446
+ enableHotReload(callback) {
447
+ return this.watchConfig(callback);
448
+ }
449
+ }
450
+ exports.ConfigManager = ConfigManager;
451
+ /**
452
+ * Default configuration
453
+ */
454
+ ConfigManager.DEFAULT_CONFIG = {
455
+ providers: {
456
+ ai: {
457
+ provider: "auto",
458
+ enableFallback: true,
459
+ enableAnalytics: false,
460
+ enableEvaluation: false,
461
+ timeout: "10m",
462
+ retryAttempts: 3,
463
+ temperature: 0.3,
464
+ maxTokens: 1000000,
465
+ },
466
+ git: {
467
+ platform: "bitbucket",
468
+ credentials: {
469
+ username: process.env.BITBUCKET_USERNAME || "",
470
+ token: process.env.BITBUCKET_TOKEN || "",
471
+ baseUrl: process.env.BITBUCKET_BASE_URL ||
472
+ "https://your-bitbucket-server.com",
473
+ },
474
+ },
475
+ },
476
+ features: {
477
+ codeReview: {
478
+ enabled: true,
479
+ severityLevels: ["CRITICAL", "MAJOR", "MINOR", "SUGGESTION"],
480
+ categories: [
481
+ "security",
482
+ "performance",
483
+ "maintainability",
484
+ "functionality",
485
+ "error_handling",
486
+ "testing",
487
+ ],
488
+ excludePatterns: ["*.lock", "*.svg", "*.min.js", "*.map"],
489
+ contextLines: 3,
490
+ systemPrompt: "You are an Expert Security Code Reviewer for enterprise applications. Your role is to:\n\n๐Ÿ”’ SECURITY FIRST: Prioritize security vulnerabilities and data protection\nโšก PERFORMANCE AWARE: Identify performance bottlenecks and optimization opportunities\n๐Ÿ—๏ธ QUALITY FOCUSED: Ensure maintainable, readable, and robust code\n๐Ÿ›ก๏ธ ERROR RESILIENT: Verify comprehensive error handling and edge cases\n\nYou provide actionable, educational feedback with specific examples and solutions.\nFocus on critical issues that could impact production systems.",
491
+ focusAreas: [
492
+ "๐Ÿ”’ Security Analysis (CRITICAL PRIORITY)",
493
+ "โšก Performance Review",
494
+ "๐Ÿ—๏ธ Code Quality",
495
+ "๐Ÿงช Testing & Error Handling",
496
+ ],
497
+ },
498
+ descriptionEnhancement: {
499
+ enabled: true,
500
+ preserveContent: true,
501
+ requiredSections: [
502
+ {
503
+ key: "changelog",
504
+ name: "Changelog (Modules Modified)",
505
+ required: true,
506
+ },
507
+ {
508
+ key: "testcases",
509
+ name: "Test Cases (What to be tested)",
510
+ required: true,
511
+ },
512
+ {
513
+ key: "config_changes",
514
+ name: "CAC Config Or Service Config Changes",
515
+ required: true,
516
+ },
517
+ ],
518
+ autoFormat: true,
519
+ systemPrompt: "You are an Expert Technical Writer specializing in pull request documentation. Your role is to:\n\n๐Ÿ“ CLARITY FIRST: Create clear, comprehensive PR descriptions that help reviewers understand the changes\n๐ŸŽฅ STORY TELLING: Explain the 'why' behind changes, not just the 'what'\n๐Ÿ“ˆ STRUCTURED: Follow consistent formatting with required sections\n๐Ÿ”— CONTEXTUAL: Link changes to business value and technical rationale\n\nCRITICAL INSTRUCTION: Return ONLY the enhanced PR description content as clean markdown. Do NOT include any meta-commentary, explanations about what you're doing, or introductory text like \"I will enhance...\" or \"Here is the enhanced description:\". \n\nOutput the enhanced description directly without any wrapper text or explanations.",
520
+ outputTemplate: "# PR Title Enhancement (if needed)\n\n## Summary\n[Clear overview of what this PR accomplishes]\n\n## Changes Made\n[Specific technical changes - be precise]\n\n## Testing\n[How the changes were tested]\n\n## Impact\n[Business/technical impact and considerations]\n\n## Additional Notes\n[Any deployment notes, follow-ups, or special considerations]",
521
+ enhancementInstructions: 'Return ONLY the enhanced PR description as clean markdown. Do NOT include any explanatory text, meta-commentary, or phrases like "Here is the enhanced description:" or "I will enhance...".\n\nStart directly with the enhanced description content using this structure:',
522
+ },
523
+ securityScan: {
524
+ enabled: true,
525
+ level: "strict",
526
+ scanTypes: ["secrets", "vulnerabilities", "dependencies"],
527
+ },
528
+ analytics: {
529
+ enabled: true,
530
+ trackMetrics: true,
531
+ exportFormat: "json",
532
+ },
533
+ },
534
+ cache: {
535
+ enabled: true,
536
+ ttl: "1h",
537
+ maxSize: "100MB",
538
+ storage: "memory",
539
+ },
540
+ performance: {
541
+ batch: {
542
+ enabled: true,
543
+ maxConcurrent: 5,
544
+ delayBetween: "1s",
545
+ },
546
+ optimization: {
547
+ reuseConnections: true,
548
+ compressRequests: true,
549
+ enableHttp2: true,
550
+ },
551
+ },
552
+ rules: {
553
+ security: [
554
+ {
555
+ name: "No hardcoded secrets",
556
+ pattern: "(password|secret|key|token)\\s*[=:]\\s*['\"][^'\"]{8,}['\"]",
557
+ severity: "CRITICAL",
558
+ message: "Hardcoded secrets detected",
559
+ suggestion: "Use environment variables or secure configuration management",
560
+ },
561
+ ],
562
+ performance: [
563
+ {
564
+ name: "Avoid N+1 queries",
565
+ pattern: "for.*\\.(find|get|query|select)",
566
+ severity: "MAJOR",
567
+ message: "Potential N+1 query pattern detected",
568
+ suggestion: "Consider using batch queries or joins",
569
+ },
570
+ ],
571
+ },
572
+ reporting: {
573
+ formats: ["markdown", "json"],
574
+ includeAnalytics: true,
575
+ includeMetrics: true,
576
+ },
577
+ monitoring: {
578
+ enabled: true,
579
+ metrics: ["performance", "cache", "api_calls"],
580
+ exportFormat: "json",
581
+ interval: "5m",
582
+ },
583
+ };
584
+ // Export singleton instance
585
+ exports.configManager = new ConfigManager();
586
+ // Export factory function
587
+ function createConfigManager() {
588
+ return new ConfigManager();
589
+ }
590
+ //# sourceMappingURL=ConfigManager.js.map
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Enhanced Logger utility - Optimized from both pr-police.js and pr-describe.js
3
+ * Provides consistent logging across all Guardian operations
4
+ */
5
+ import { Logger as ILogger, LogLevel, LoggerOptions } from "../types";
6
+ export declare class Logger implements ILogger {
7
+ private options;
8
+ constructor(options?: Partial<LoggerOptions>);
9
+ private shouldLog;
10
+ private formatMessage;
11
+ private colorize;
12
+ debug(message: string, ...args: any[]): void;
13
+ info(message: string, ...args: any[]): void;
14
+ warn(message: string, ...args: any[]): void;
15
+ error(message: string, ...args: any[]): void;
16
+ badge(): void;
17
+ phase(message: string): void;
18
+ success(message: string): void;
19
+ operation(operation: string, status: "started" | "completed" | "failed"): void;
20
+ violation(severity: string, message: string, file?: string): void;
21
+ progress(current: number, total: number, operation: string): void;
22
+ private createProgressBar;
23
+ child(context: Record<string, any>): Logger;
24
+ setLevel(level: LogLevel): void;
25
+ setVerbose(verbose: boolean): void;
26
+ getConfig(): LoggerOptions;
27
+ }
28
+ export declare const logger: Logger;
29
+ export declare function createLogger(options?: Partial<LoggerOptions>): Logger;
30
+ //# sourceMappingURL=Logger.d.ts.map