claude-flow-novice 2.18.24 → 2.18.26

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,612 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Enhanced Post-Edit Pipeline - Comprehensive Validation Hook
4
+ * Validates edited files with TypeScript, ESLint, Prettier, Security Analysis, and Code Metrics
5
+ *
6
+ * Features:
7
+ * - TypeScript validation with error categorization
8
+ * - ESLint integration for code quality
9
+ * - Prettier formatting checks
10
+ * - Security analysis (eval, password logging, XSS detection)
11
+ * - Code metrics (lines, functions, classes, complexity)
12
+ * - Actionable recommendations engine
13
+ *
14
+ * Usage: node config/hooks/post-edit-pipeline.js <file_path> [--memory-key <key>] [--agent-id <id>]
15
+ */
16
+
17
+ import { execSync } from 'child_process';
18
+ import { existsSync, readFileSync, appendFileSync, mkdirSync } from 'fs';
19
+ import { dirname, extname, resolve } from 'path';
20
+
21
+ // Parse arguments
22
+ const args = process.argv.slice(2);
23
+ const filePath = args[0];
24
+ const memoryKeyIndex = args.indexOf('--memory-key');
25
+ const memoryKey = memoryKeyIndex >= 0 ? args[memoryKeyIndex + 1] : null;
26
+ const agentIdIndex = args.indexOf('--agent-id');
27
+ const agentId = agentIdIndex >= 0 ? args[agentIdIndex + 1] : null;
28
+
29
+ if (!filePath) {
30
+ console.error('Error: File path required');
31
+ console.error('Usage: node config/hooks/post-edit-pipeline.js <file_path> [--memory-key <key>] [--agent-id <id>]');
32
+ process.exit(1);
33
+ }
34
+
35
+ // Ensure log directory exists
36
+ const logDir = '.artifacts/logs';
37
+ if (!existsSync(logDir)) {
38
+ mkdirSync(logDir, { recursive: true });
39
+ }
40
+
41
+ const logFile = `${logDir}/post-edit-pipeline.log`;
42
+
43
+ function log(status, message, metadata = {}) {
44
+ const entry = JSON.stringify({
45
+ timestamp: new Date().toISOString(),
46
+ file: filePath,
47
+ status,
48
+ message,
49
+ memoryKey,
50
+ agentId,
51
+ ...metadata
52
+ });
53
+ console.log(entry);
54
+ appendFileSync(logFile, entry + '\n');
55
+ }
56
+
57
+ // Check if file exists
58
+ if (!existsSync(filePath)) {
59
+ log('ERROR', 'File not found', { path: filePath });
60
+ process.exit(1);
61
+ }
62
+
63
+ // Read file content for analysis
64
+ const fileContent = readFileSync(filePath, 'utf-8');
65
+ const ext = extname(filePath);
66
+
67
+ // Initialize results object
68
+ const results = {
69
+ typescript: null,
70
+ eslint: null,
71
+ prettier: null,
72
+ security: null,
73
+ metrics: null,
74
+ recommendations: []
75
+ };
76
+
77
+ // ============================================================================
78
+ // PHASE 1: TypeScript Validation
79
+ // ============================================================================
80
+
81
+ if (['.ts', '.tsx'].includes(ext)) {
82
+ try {
83
+ log('VALIDATING', 'Running TypeScript validation');
84
+
85
+ // Use tsc to check only this file
86
+ const cmd = `npx tsc --noEmit --skipLibCheck ${filePath}`;
87
+ execSync(cmd, { stdio: 'pipe', encoding: 'utf-8' });
88
+
89
+ results.typescript = {
90
+ passed: true,
91
+ errors: []
92
+ };
93
+ log('SUCCESS', 'TypeScript validation passed');
94
+
95
+ } catch (error) {
96
+ // Parse TypeScript errors
97
+ const output = error.stdout || error.stderr || '';
98
+ const lines = output.split('\n').filter(line => line.includes('error TS'));
99
+
100
+ if (lines.length === 0) {
101
+ results.typescript = {
102
+ passed: true,
103
+ errors: []
104
+ };
105
+ log('SUCCESS', 'No TypeScript errors detected');
106
+ } else {
107
+ // Categorize errors
108
+ const errorTypes = {
109
+ implicitAny: lines.filter(l => l.includes('TS7006') || l.includes('TS7031')).length,
110
+ propertyMissing: lines.filter(l => l.includes('TS2339')).length,
111
+ typeMismatch: lines.filter(l => l.includes('TS2322') || l.includes('TS2345')).length,
112
+ syntaxError: lines.filter(l => l.includes('TS1005') || l.includes('TS1128')).length,
113
+ other: 0
114
+ };
115
+ errorTypes.other = lines.length - Object.values(errorTypes).reduce((a, b) => a + b, 0);
116
+
117
+ const severity = errorTypes.syntaxError > 0 ? 'SYNTAX_ERROR' :
118
+ lines.length > 5 ? 'LINT_ISSUES' : 'TYPE_WARNING';
119
+
120
+ results.typescript = {
121
+ passed: false,
122
+ errorCount: lines.length,
123
+ errorTypes,
124
+ errors: lines.slice(0, 5),
125
+ severity
126
+ };
127
+
128
+ log(severity, `TypeScript errors detected: ${lines.length}`, {
129
+ errorCount: lines.length,
130
+ errorTypes,
131
+ errors: lines.slice(0, 5)
132
+ });
133
+
134
+ // Add recommendations based on error types
135
+ if (errorTypes.syntaxError > 0) {
136
+ results.recommendations.push({
137
+ type: 'typescript',
138
+ priority: 'critical',
139
+ message: 'Fix syntax errors before proceeding',
140
+ action: 'Review and fix TypeScript syntax issues'
141
+ });
142
+ } else if (errorTypes.implicitAny > 0) {
143
+ results.recommendations.push({
144
+ type: 'typescript',
145
+ priority: 'high',
146
+ message: 'Add explicit type annotations',
147
+ action: 'Add type annotations for parameters and return values'
148
+ });
149
+ }
150
+ }
151
+ }
152
+ } else {
153
+ log('SKIPPED', 'TypeScript validation skipped for non-TypeScript file');
154
+ }
155
+
156
+ // ============================================================================
157
+ // PHASE 1: ESLint Integration
158
+ // ============================================================================
159
+
160
+ if (['.js', '.jsx', '.ts', '.tsx'].includes(ext)) {
161
+ try {
162
+ log('VALIDATING', 'Running ESLint validation');
163
+
164
+ // Check if ESLint is available
165
+ try {
166
+ execSync('npx eslint --version', { stdio: 'ignore', timeout: 5000 });
167
+ } catch {
168
+ log('SKIPPED', 'ESLint not available');
169
+ results.eslint = { available: false };
170
+ }
171
+
172
+ if (results.eslint === null) {
173
+ // Run ESLint
174
+ const eslintCmd = `npx eslint "${filePath}" --format json`;
175
+ const eslintOutput = execSync(eslintCmd, {
176
+ stdio: 'pipe',
177
+ encoding: 'utf-8',
178
+ timeout: 10000
179
+ });
180
+
181
+ const eslintResults = JSON.parse(eslintOutput);
182
+ const fileResults = eslintResults[0] || { messages: [] };
183
+
184
+ results.eslint = {
185
+ available: true,
186
+ passed: fileResults.errorCount === 0,
187
+ errorCount: fileResults.errorCount || 0,
188
+ warningCount: fileResults.warningCount || 0,
189
+ messages: fileResults.messages.slice(0, 5)
190
+ };
191
+
192
+ if (fileResults.errorCount > 0) {
193
+ log('LINT_ISSUES', `ESLint found ${fileResults.errorCount} errors`, {
194
+ errorCount: fileResults.errorCount,
195
+ warningCount: fileResults.warningCount
196
+ });
197
+
198
+ results.recommendations.push({
199
+ type: 'eslint',
200
+ priority: 'high',
201
+ message: `Fix ${fileResults.errorCount} ESLint errors`,
202
+ action: `Run: npx eslint "${filePath}" --fix`
203
+ });
204
+ } else {
205
+ log('SUCCESS', 'ESLint validation passed');
206
+ }
207
+ }
208
+ } catch (error) {
209
+ // ESLint errors are expected, parse them
210
+ if (error.stdout) {
211
+ try {
212
+ const eslintResults = JSON.parse(error.stdout);
213
+ const fileResults = eslintResults[0] || { messages: [] };
214
+
215
+ results.eslint = {
216
+ available: true,
217
+ passed: fileResults.errorCount === 0,
218
+ errorCount: fileResults.errorCount || 0,
219
+ warningCount: fileResults.warningCount || 0,
220
+ messages: fileResults.messages.slice(0, 5)
221
+ };
222
+
223
+ if (fileResults.errorCount > 0) {
224
+ log('LINT_ISSUES', `ESLint found ${fileResults.errorCount} errors`, {
225
+ errorCount: fileResults.errorCount,
226
+ warningCount: fileResults.warningCount
227
+ });
228
+
229
+ results.recommendations.push({
230
+ type: 'eslint',
231
+ priority: 'high',
232
+ message: `Fix ${fileResults.errorCount} ESLint errors`,
233
+ action: `Run: npx eslint "${filePath}" --fix`
234
+ });
235
+ }
236
+ } catch {
237
+ log('ERROR', 'ESLint execution failed', { error: error.message });
238
+ results.eslint = { available: false, error: error.message };
239
+ }
240
+ } else {
241
+ log('ERROR', 'ESLint execution failed', { error: error.message });
242
+ results.eslint = { available: false, error: error.message };
243
+ }
244
+ }
245
+ }
246
+
247
+ // ============================================================================
248
+ // PHASE 1: Prettier Integration
249
+ // ============================================================================
250
+
251
+ if (['.js', '.jsx', '.ts', '.tsx', '.json', '.css', '.html'].includes(ext)) {
252
+ try {
253
+ log('VALIDATING', 'Running Prettier formatting check');
254
+
255
+ // Check if Prettier is available
256
+ try {
257
+ execSync('npx prettier --version', { stdio: 'ignore', timeout: 5000 });
258
+ } catch {
259
+ log('SKIPPED', 'Prettier not available');
260
+ results.prettier = { available: false };
261
+ }
262
+
263
+ if (results.prettier === null) {
264
+ // Run Prettier check
265
+ try {
266
+ const prettierCmd = `npx prettier --check "${filePath}"`;
267
+ execSync(prettierCmd, {
268
+ stdio: 'pipe',
269
+ encoding: 'utf-8',
270
+ timeout: 10000
271
+ });
272
+
273
+ results.prettier = {
274
+ available: true,
275
+ passed: true,
276
+ formatted: true
277
+ };
278
+ log('SUCCESS', 'Prettier formatting check passed');
279
+
280
+ } catch (error) {
281
+ results.prettier = {
282
+ available: true,
283
+ passed: false,
284
+ formatted: false,
285
+ needsFormatting: true
286
+ };
287
+ log('LINT_ISSUES', 'File needs Prettier formatting');
288
+
289
+ results.recommendations.push({
290
+ type: 'prettier',
291
+ priority: 'medium',
292
+ message: 'File needs formatting',
293
+ action: `Run: npx prettier --write "${filePath}"`
294
+ });
295
+ }
296
+ }
297
+ } catch (error) {
298
+ log('ERROR', 'Prettier execution failed', { error: error.message });
299
+ results.prettier = { available: false, error: error.message };
300
+ }
301
+ }
302
+
303
+ // ============================================================================
304
+ // PHASE 2: Security Analysis
305
+ // ============================================================================
306
+
307
+ log('VALIDATING', 'Running security analysis');
308
+
309
+ const securityIssues = [];
310
+
311
+ // Check for eval() usage
312
+ if (fileContent.includes('eval(')) {
313
+ securityIssues.push({
314
+ type: 'security',
315
+ severity: 'critical',
316
+ message: 'Use of eval() function detected - security risk',
317
+ suggestion: 'Replace eval() with safer alternatives like JSON.parse() or Function constructor with proper validation'
318
+ });
319
+ }
320
+
321
+ // Check for new Function() usage
322
+ if (fileContent.includes('new Function(')) {
323
+ securityIssues.push({
324
+ type: 'security',
325
+ severity: 'critical',
326
+ message: 'Use of new Function() detected - security risk',
327
+ suggestion: 'Avoid dynamic code execution; use safer alternatives'
328
+ });
329
+ }
330
+
331
+ // Check for password logging
332
+ if (fileContent.includes('password') && (fileContent.includes('console.log') || fileContent.includes('console.debug'))) {
333
+ securityIssues.push({
334
+ type: 'security',
335
+ severity: 'critical',
336
+ message: 'Potential password logging detected',
337
+ suggestion: 'Remove password logging from code immediately'
338
+ });
339
+ }
340
+
341
+ // Check for XSS vulnerabilities (innerHTML with concatenation)
342
+ if (fileContent.includes('innerHTML') && fileContent.match(/innerHTML\s*[+]=|innerHTML\s*=\s*.*\+/)) {
343
+ securityIssues.push({
344
+ type: 'security',
345
+ severity: 'high',
346
+ message: 'Potential XSS vulnerability with innerHTML concatenation',
347
+ suggestion: 'Use textContent, createElement, or proper sanitization libraries'
348
+ });
349
+ }
350
+
351
+ // Check for hardcoded secrets (basic patterns)
352
+ const secretPatterns = [
353
+ /api[_-]?key\s*=\s*['"][^'"]{20,}['"]/i,
354
+ /secret\s*=\s*['"][^'"]{20,}['"]/i,
355
+ /token\s*=\s*['"][^'"]{20,}['"]/i,
356
+ /password\s*=\s*['"][^'"]+['"]/i
357
+ ];
358
+
359
+ for (const pattern of secretPatterns) {
360
+ if (pattern.test(fileContent)) {
361
+ securityIssues.push({
362
+ type: 'security',
363
+ severity: 'critical',
364
+ message: 'Potential hardcoded secret detected',
365
+ suggestion: 'Move secrets to environment variables or secure configuration'
366
+ });
367
+ break; // Only report once
368
+ }
369
+ }
370
+
371
+ results.security = {
372
+ passed: securityIssues.length === 0,
373
+ issueCount: securityIssues.length,
374
+ issues: securityIssues
375
+ };
376
+
377
+ if (securityIssues.length > 0) {
378
+ log('SECURITY_ISSUES', `Security analysis found ${securityIssues.length} issues`, {
379
+ issueCount: securityIssues.length,
380
+ issues: securityIssues
381
+ });
382
+
383
+ results.recommendations.push({
384
+ type: 'security',
385
+ priority: 'critical',
386
+ message: `Address ${securityIssues.length} security ${securityIssues.length === 1 ? 'issue' : 'issues'} immediately`,
387
+ action: 'Review security recommendations and apply fixes'
388
+ });
389
+ } else {
390
+ log('SUCCESS', 'No security issues detected');
391
+ }
392
+
393
+ // ============================================================================
394
+ // PHASE 3: Code Metrics
395
+ // ============================================================================
396
+
397
+ log('VALIDATING', 'Calculating code metrics');
398
+
399
+ const lines = fileContent.split('\n');
400
+ const lineCount = lines.length;
401
+ const functionCount = (fileContent.match(/function\s+\w+|const\s+\w+\s*=\s*\([^)]*\)\s*=>/g) || []).length;
402
+ const classCount = (fileContent.match(/class\s+\w+/g) || []).length;
403
+ const todoCount = (fileContent.match(/\/\/\s*TODO|\/\*\s*TODO/gi) || []).length;
404
+ const fixmeCount = (fileContent.match(/\/\/\s*FIXME|\/\*\s*FIXME/gi) || []).length;
405
+
406
+ // Calculate cyclomatic complexity (simplified)
407
+ let complexity = 'low';
408
+ if (lineCount > 300 || functionCount > 10) {
409
+ complexity = 'high';
410
+ } else if (lineCount > 150 || functionCount > 5) {
411
+ complexity = 'medium';
412
+ }
413
+
414
+ results.metrics = {
415
+ lines: lineCount,
416
+ functions: functionCount,
417
+ classes: classCount,
418
+ todos: todoCount,
419
+ fixmes: fixmeCount,
420
+ complexity
421
+ };
422
+
423
+ log('SUCCESS', 'Code metrics calculated', {
424
+ lines: lineCount,
425
+ functions: functionCount,
426
+ classes: classCount,
427
+ complexity
428
+ });
429
+
430
+ // ============================================================================
431
+ // PHASE 3: Recommendations Engine
432
+ // ============================================================================
433
+
434
+ log('VALIDATING', 'Generating recommendations');
435
+
436
+ // Maintainability recommendations
437
+ if (lineCount > 200) {
438
+ results.recommendations.push({
439
+ type: 'maintainability',
440
+ priority: 'medium',
441
+ message: `File has ${lineCount} lines - consider breaking it down`,
442
+ action: 'Split into smaller, focused modules (150-200 lines per file)'
443
+ });
444
+ }
445
+
446
+ // Code quality recommendations
447
+ if (fileContent.includes('var ')) {
448
+ results.recommendations.push({
449
+ type: 'code-quality',
450
+ priority: 'low',
451
+ message: 'Use const or let instead of var',
452
+ action: 'Replace var declarations with const or let for better scoping'
453
+ });
454
+ }
455
+
456
+ if (fileContent.includes('==') && !fileContent.includes('===') && fileContent.includes('==') > fileContent.includes('===')) {
457
+ results.recommendations.push({
458
+ type: 'code-quality',
459
+ priority: 'medium',
460
+ message: 'Prefer strict equality (===) over loose equality (==)',
461
+ action: 'Replace == with === for type-safe comparisons'
462
+ });
463
+ }
464
+
465
+ // TypeScript-specific recommendations
466
+ if (['.ts', '.tsx'].includes(ext)) {
467
+ if (fileContent.includes(': any')) {
468
+ results.recommendations.push({
469
+ type: 'typescript',
470
+ priority: 'medium',
471
+ message: 'Avoid using "any" type when possible',
472
+ action: 'Use specific types or unknown for better type safety'
473
+ });
474
+ }
475
+
476
+ if (!fileContent.includes('interface') && !fileContent.includes('type ') && lineCount > 100) {
477
+ results.recommendations.push({
478
+ type: 'typescript',
479
+ priority: 'low',
480
+ message: 'Consider defining interfaces or types',
481
+ action: 'Add type definitions for better code structure and maintainability'
482
+ });
483
+ }
484
+ }
485
+
486
+ // Documentation recommendations
487
+ if (fileContent.includes('export ') && !fileContent.includes('/**') && functionCount > 0) {
488
+ results.recommendations.push({
489
+ type: 'documentation',
490
+ priority: 'low',
491
+ message: 'Public exports could benefit from JSDoc comments',
492
+ action: 'Add JSDoc documentation for exported functions/classes'
493
+ });
494
+ }
495
+
496
+ // Testing recommendations
497
+ if (!filePath.includes('test') && !filePath.includes('spec') && (functionCount > 0 || classCount > 0)) {
498
+ results.recommendations.push({
499
+ type: 'testing',
500
+ priority: 'medium',
501
+ message: 'Consider writing tests for this module',
502
+ action: 'Create corresponding test file to ensure code reliability'
503
+ });
504
+ }
505
+
506
+ // TODO/FIXME recommendations
507
+ if (todoCount > 0 || fixmeCount > 0) {
508
+ results.recommendations.push({
509
+ type: 'maintenance',
510
+ priority: 'low',
511
+ message: `Found ${todoCount} TODOs and ${fixmeCount} FIXMEs`,
512
+ action: 'Address pending tasks and technical debt'
513
+ });
514
+ }
515
+
516
+ // Sort recommendations by priority
517
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
518
+ results.recommendations.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
519
+
520
+ // Limit to top 10 recommendations
521
+ results.recommendations = results.recommendations.slice(0, 10);
522
+
523
+ log('SUCCESS', `Generated ${results.recommendations.length} recommendations`);
524
+
525
+ // ============================================================================
526
+ // Summary and Exit
527
+ // ============================================================================
528
+
529
+ // Calculate overall status
530
+ const hasCriticalIssues = results.recommendations.some(r => r.priority === 'critical');
531
+ const hasHighIssues = results.recommendations.some(r => r.priority === 'high');
532
+ const hasTypeScriptErrors = results.typescript && !results.typescript.passed;
533
+ const hasESLintErrors = results.eslint && results.eslint.errorCount > 0;
534
+
535
+ let overallStatus = 'SUCCESS';
536
+ let exitCode = 0;
537
+
538
+ if (hasCriticalIssues) {
539
+ overallStatus = 'CRITICAL_ISSUES';
540
+ exitCode = 2;
541
+ } else if (hasTypeScriptErrors && results.typescript.severity === 'SYNTAX_ERROR') {
542
+ overallStatus = 'SYNTAX_ERROR';
543
+ exitCode = 2;
544
+ } else if (hasHighIssues || hasESLintErrors) {
545
+ overallStatus = 'LINT_ISSUES';
546
+ exitCode = 0; // Non-blocking
547
+ } else if (results.recommendations.length > 0) {
548
+ overallStatus = 'IMPROVEMENTS_SUGGESTED';
549
+ exitCode = 0;
550
+ }
551
+
552
+ // Log final summary
553
+ log(overallStatus, 'Pipeline validation complete', {
554
+ typescript: results.typescript,
555
+ eslint: results.eslint,
556
+ prettier: results.prettier,
557
+ security: results.security,
558
+ metrics: results.metrics,
559
+ recommendationCount: results.recommendations.length,
560
+ topRecommendations: results.recommendations.slice(0, 3)
561
+ });
562
+
563
+ // Print user-friendly summary
564
+ console.error('\n' + '='.repeat(80));
565
+ console.error('Enhanced Post-Edit Pipeline Summary');
566
+ console.error('='.repeat(80));
567
+
568
+ if (results.typescript) {
569
+ console.error(`\nTypeScript: ${results.typescript.passed ? '✅ PASSED' : '❌ FAILED'}`);
570
+ if (!results.typescript.passed) {
571
+ console.error(` Errors: ${results.typescript.errorCount}`);
572
+ }
573
+ }
574
+
575
+ if (results.eslint && results.eslint.available) {
576
+ console.error(`ESLint: ${results.eslint.passed ? '✅ PASSED' : '❌ FAILED'}`);
577
+ if (!results.eslint.passed) {
578
+ console.error(` Errors: ${results.eslint.errorCount}, Warnings: ${results.eslint.warningCount}`);
579
+ }
580
+ }
581
+
582
+ if (results.prettier && results.prettier.available) {
583
+ console.error(`Prettier: ${results.prettier.passed ? '✅ PASSED' : '⚠️ NEEDS FORMATTING'}`);
584
+ }
585
+
586
+ console.error(`\nSecurity: ${results.security.passed ? '✅ NO ISSUES' : '❌ ISSUES FOUND'}`);
587
+ if (!results.security.passed) {
588
+ console.error(` Issues: ${results.security.issueCount}`);
589
+ }
590
+
591
+ console.error(`\nCode Metrics:`);
592
+ console.error(` Lines: ${results.metrics.lines}`);
593
+ console.error(` Functions: ${results.metrics.functions}`);
594
+ console.error(` Classes: ${results.metrics.classes}`);
595
+ console.error(` Complexity: ${results.metrics.complexity.toUpperCase()}`);
596
+
597
+ if (results.recommendations.length > 0) {
598
+ console.error(`\nTop Recommendations:`);
599
+ results.recommendations.slice(0, 3).forEach((rec, i) => {
600
+ const icon = rec.priority === 'critical' ? '🔴' : rec.priority === 'high' ? '🟠' : rec.priority === 'medium' ? '🟡' : '🔵';
601
+ console.error(` ${icon} [${rec.type.toUpperCase()}] ${rec.message}`);
602
+ console.error(` Action: ${rec.action}`);
603
+ });
604
+
605
+ if (results.recommendations.length > 3) {
606
+ console.error(` ... and ${results.recommendations.length - 3} more recommendations`);
607
+ }
608
+ }
609
+
610
+ console.error('='.repeat(80) + '\n');
611
+
612
+ process.exit(exitCode);
@@ -0,0 +1,24 @@
1
+ # Kong Grafana Datasource Configuration
2
+ apiVersion: 1
3
+
4
+ datasources:
5
+ - name: Prometheus
6
+ type: prometheus
7
+ access: proxy
8
+ url: http://kong-prometheus:9090
9
+ isDefault: true
10
+ editable: true
11
+ jsonData:
12
+ timeInterval: "15s"
13
+ queryTimeout: "60s"
14
+ httpMethod: "POST"
15
+
16
+ - name: Kong-Redis
17
+ type: redis-datasource
18
+ access: proxy
19
+ url: redis://kong-redis:6379
20
+ editable: true
21
+ jsonData:
22
+ poolSize: 5
23
+ timeout: 10
24
+ pingInterval: 30