@devilsdev/rag-pipeline-utils 2.1.11 → 2.2.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 (110) hide show
  1. package/.ragrc.schema.json +218 -0
  2. package/README.md +651 -393
  3. package/bin/cli.js +8 -14
  4. package/dist/index.d.ts +237 -0
  5. package/dist/index.mjs +22 -7
  6. package/package.json +17 -13
  7. package/src/ai/adaptive-retrieval.js +191 -0
  8. package/src/ai/federated-learning.js +214 -0
  9. package/src/ai/federation/federated-learning-coordinator.js +345 -0
  10. package/src/ai/federation.js +76 -0
  11. package/src/ai/index-fixed.js +509 -0
  12. package/src/ai/index.js +33 -0
  13. package/src/ai/model-trainer.js +217 -0
  14. package/src/ai/model-training.js +205 -0
  15. package/src/ai/module-template.js +37 -0
  16. package/src/ai/multimodal/multi-modal-processor.js +345 -0
  17. package/src/ai/multimodal-processing.js +268 -0
  18. package/src/ai/multimodal.js +71 -0
  19. package/src/ai/orchestrator.js +70 -0
  20. package/src/ai/retrieval/adaptive-retrieval-engine.js +398 -0
  21. package/src/ai/retrieval-engine.js +69 -0
  22. package/src/ai/training/model-training-orchestrator.js +228 -0
  23. package/src/ci/workflow-analyzer.js +71 -0
  24. package/src/cli/commands/ai-ml.js +967 -0
  25. package/src/cli/commands/docs.js +228 -0
  26. package/src/cli/commands/dx.js +538 -0
  27. package/src/cli/commands/plugin-hub.js +875 -0
  28. package/src/cli/doctor-command.js +639 -0
  29. package/src/cli/enhanced-cli-commands.js +945 -0
  30. package/src/cli/interactive-wizard.js +997 -0
  31. package/src/cli/plugin-marketplace-commands.js +869 -0
  32. package/src/cli.js +17 -0
  33. package/src/config/enhanced-ragrc-schema.js +632 -0
  34. package/src/config/load-config.js +54 -0
  35. package/src/config/load-plugin-config.js +69 -0
  36. package/src/config/normalize-config.js +105 -0
  37. package/src/config/schema-validator.js +315 -0
  38. package/src/config/validate-plugin-schema.js +35 -0
  39. package/src/config/validate-schema.js +113 -0
  40. package/src/contracts/plugin-contracts.js +33 -0
  41. package/src/core/create-pipeline.js +99 -0
  42. package/src/core/dependency-injection.js +242 -0
  43. package/src/core/observability/event-logger.js +241 -0
  44. package/src/core/observability/instrumented-pipeline.js +609 -0
  45. package/src/core/observability/metrics.js +497 -0
  46. package/src/core/observability/tracing.js +781 -0
  47. package/src/core/performance/benchmark.js +516 -0
  48. package/src/core/performance/parallel-processor.js +240 -0
  49. package/src/core/performance/streaming-safeguards.js +611 -0
  50. package/src/core/pipeline-factory.js +60 -0
  51. package/src/core/plugin-contracts.js +89 -0
  52. package/src/core/plugin-marketplace/plugin-metadata.js +485 -0
  53. package/src/core/plugin-marketplace/plugin-publisher.js +574 -0
  54. package/src/core/plugin-marketplace/plugin-registry-format.js +419 -0
  55. package/src/core/plugin-marketplace/version-resolver.js +61 -0
  56. package/src/core/plugin-registry.js +265 -0
  57. package/src/dag/dag-engine.js +954 -0
  58. package/src/dag/dag-node.js +76 -0
  59. package/src/dag/enhanced-dag-engine.js +822 -0
  60. package/src/dx/index.js +40 -0
  61. package/src/dx/integration-templates.js +665 -0
  62. package/src/dx/performance-profiler.js +968 -0
  63. package/src/dx/realtime-debugger.js +755 -0
  64. package/src/dx/visual-pipeline-builder.js +578 -0
  65. package/src/ecosystem/plugin-analytics-dashboard.js +778 -0
  66. package/src/ecosystem/plugin-certification.js +580 -0
  67. package/src/ecosystem/plugin-hub.js +821 -0
  68. package/src/enterprise/audit-logging.js +967 -0
  69. package/src/enterprise/data-governance.js +408 -0
  70. package/src/enterprise/multi-tenancy.js +496 -0
  71. package/src/enterprise/sso-integration.js +903 -0
  72. package/src/evaluate/evaluator.js +52 -0
  73. package/src/evaluate/scoring.js +91 -0
  74. package/src/index.js +67 -0
  75. package/src/ingest.js +59 -0
  76. package/src/loader/csv-loader.js +38 -0
  77. package/src/loader/directory-loader.js +48 -0
  78. package/src/loader/html-loader.js +62 -0
  79. package/src/loader/markdown-loader.js +67 -0
  80. package/src/mocks/openai-embedder.js +119 -0
  81. package/src/mocks/openai-llm.js +77 -0
  82. package/src/mocks/pdf-loader.js +21 -0
  83. package/src/mocks/pinecone-retriever.js +106 -0
  84. package/src/observability/audit-logger.js +758 -0
  85. package/src/observability/dashboard-config.json +675 -0
  86. package/src/observability/event-logger.js +310 -0
  87. package/src/observability/metrics.js +691 -0
  88. package/src/observability/security-monitor.js +830 -0
  89. package/src/observability/slo-monitor.js +319 -0
  90. package/src/observability/telemetry.js +686 -0
  91. package/src/pipeline/pipeline.js +18 -0
  92. package/src/plugins/sample-embedder.js +53 -0
  93. package/src/plugins/sample-llm.js +30 -0
  94. package/src/plugins/sample-loader.js +24 -0
  95. package/src/plugins/sample-retriever.js +84 -0
  96. package/src/query.js +117 -0
  97. package/src/reranker/llm-reranker.js +85 -0
  98. package/src/scripts/script-utilities.js +36 -0
  99. package/src/security/plugin-sandbox.js +586 -0
  100. package/src/security/plugin-signature-verifier.js +414 -0
  101. package/src/security/validator.js +36 -0
  102. package/src/utils/ci/diagnostic-reporter.js +57 -0
  103. package/src/utils/logger.js +211 -0
  104. package/src/utils/performance-monitor.js +561 -0
  105. package/src/utils/plugin-scaffolder.js +447 -0
  106. package/src/utils/retry-policy.js +443 -0
  107. package/src/utils/retry.js +107 -0
  108. package/src/utils/safe-expression-evaluator.js +579 -0
  109. package/src/utils/structured-logger.js +361 -0
  110. package/src/utils/validate-plugin-contract.js +42 -0
@@ -0,0 +1,414 @@
1
+ /**
2
+ * @fileoverview Plugin Signature Verifier
3
+ * Verifies Ed25519 digital signatures for plugin manifests to ensure authenticity
4
+ * Enforces fail-closed verification for production security
5
+ *
6
+ * @author DevilsDev Team
7
+ * @since 2.1.8
8
+ */
9
+
10
+ const crypto = require('crypto');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ /**
15
+ * Plugin signature verification with Ed25519
16
+ */
17
+ class PluginSignatureVerifier {
18
+ constructor(options = {}) {
19
+ this.enabled = options.enabled !== false;
20
+ this.trustStore = options.trustStore || new Map();
21
+ this.auditLogger =
22
+ options.auditLogger || this._defaultAuditLogger.bind(this);
23
+ this.failClosed = options.failClosed !== false; // Default to fail-closed
24
+
25
+ // Load trusted public keys
26
+ this._loadTrustedKeys(options.trustedKeysPath);
27
+ }
28
+
29
+ /**
30
+ * Load trusted public keys from configuration
31
+ * @param {string} keysPath - Path to trusted keys file
32
+ */
33
+ _loadTrustedKeys(keysPath) {
34
+ if (!keysPath) {
35
+ // Default trusted keys for the DevilsDev organization
36
+ this.trustStore.set('devilsdev-official', {
37
+ publicKey: 'your-ed25519-public-key-here',
38
+ name: 'DevilsDev Official Plugins',
39
+ verified: true,
40
+ });
41
+ return;
42
+ }
43
+
44
+ try {
45
+ if (fs.existsSync(keysPath)) {
46
+ const keysData = fs.readFileSync(keysPath, 'utf8');
47
+ const trustedKeys = JSON.parse(keysData);
48
+
49
+ for (const [keyId, keyInfo] of Object.entries(trustedKeys)) {
50
+ if (keyInfo.publicKey && keyInfo.name) {
51
+ this.trustStore.set(keyId, keyInfo);
52
+ }
53
+ }
54
+ }
55
+ } catch (error) {
56
+ if (this.failClosed) {
57
+ throw new Error(`Failed to load trusted keys: ${error.message}`);
58
+ }
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Verify plugin signature
64
+ * @param {object} manifest - Plugin manifest
65
+ * @param {string} signature - Base64 encoded signature
66
+ * @param {string} signerId - Signer identifier
67
+ * @returns {Promise<object>} Verification result
68
+ */
69
+ async verifyPluginSignature(manifest, signature, signerId) {
70
+ const verificationResult = {
71
+ verified: false,
72
+ signerId,
73
+ timestamp: new Date().toISOString(),
74
+ error: null,
75
+ auditTrail: [],
76
+ };
77
+
78
+ try {
79
+ if (!this.enabled) {
80
+ verificationResult.error = 'Signature verification disabled';
81
+ this._auditVerification(verificationResult, manifest);
82
+
83
+ if (this.failClosed) {
84
+ throw new Error(
85
+ 'Plugin signature verification is disabled in fail-closed mode',
86
+ );
87
+ }
88
+ return verificationResult;
89
+ }
90
+
91
+ // Validate inputs
92
+ if (!manifest || typeof manifest !== 'object') {
93
+ throw new Error('Invalid manifest: must be an object');
94
+ }
95
+
96
+ if (!signature || typeof signature !== 'string') {
97
+ throw new Error('Invalid signature: must be a non-empty string');
98
+ }
99
+
100
+ if (!signerId || typeof signerId !== 'string') {
101
+ throw new Error('Invalid signerId: must be a non-empty string');
102
+ }
103
+
104
+ // Check if signer is trusted
105
+ const trustedKey = this.trustStore.get(signerId);
106
+ if (!trustedKey) {
107
+ throw new Error(`Untrusted signer: ${signerId}`);
108
+ }
109
+
110
+ // Canonicalize manifest for verification
111
+ const canonicalManifest = this._canonicalizeManifest(manifest);
112
+ const manifestData = Buffer.from(
113
+ JSON.stringify(canonicalManifest),
114
+ 'utf8',
115
+ );
116
+
117
+ // Decode signature
118
+ let signatureBuffer;
119
+ try {
120
+ signatureBuffer = Buffer.from(signature, 'base64');
121
+ } catch (error) {
122
+ throw new Error(`Invalid signature encoding: ${error.message}`);
123
+ }
124
+
125
+ // Verify signature using Web Crypto API or Node.js crypto
126
+ const isValid = await this._verifyEd25519Signature(
127
+ manifestData,
128
+ signatureBuffer,
129
+ trustedKey.publicKey,
130
+ );
131
+
132
+ if (!isValid) {
133
+ throw new Error('Signature verification failed');
134
+ }
135
+
136
+ verificationResult.verified = true;
137
+ verificationResult.auditTrail.push({
138
+ action: 'signature_verified',
139
+ signerId,
140
+ timestamp: new Date().toISOString(),
141
+ manifestHash: this._hashManifest(canonicalManifest),
142
+ });
143
+ } catch (error) {
144
+ verificationResult.error = error.message;
145
+ verificationResult.auditTrail.push({
146
+ action: 'verification_failed',
147
+ signerId,
148
+ timestamp: new Date().toISOString(),
149
+ error: error.message,
150
+ });
151
+
152
+ if (this.failClosed) {
153
+ this._auditVerification(verificationResult, manifest);
154
+ throw new Error(
155
+ `Plugin signature verification failed: ${error.message}`,
156
+ );
157
+ }
158
+ }
159
+
160
+ this._auditVerification(verificationResult, manifest);
161
+ return verificationResult;
162
+ }
163
+
164
+ /**
165
+ * Verify Ed25519 signature
166
+ * @param {Buffer} data - Data that was signed
167
+ * @param {Buffer} signature - Signature bytes
168
+ * @param {string} publicKeyHex - Public key in hex format
169
+ * @returns {Promise<boolean>} Verification result
170
+ */
171
+ async _verifyEd25519Signature(data, signature, publicKeyHex) {
172
+ try {
173
+ // Convert hex public key to buffer
174
+ const publicKeyBuffer = Buffer.from(publicKeyHex, 'hex');
175
+
176
+ // Use Node.js crypto for Ed25519 verification
177
+ const verify = crypto.createVerify('Ed25519');
178
+ verify.update(data);
179
+
180
+ // Create public key object
181
+ const publicKeyObject = crypto.createPublicKey({
182
+ key: publicKeyBuffer,
183
+ format: 'der',
184
+ type: 'spki',
185
+ });
186
+
187
+ return verify.verify(publicKeyObject, signature);
188
+ } catch (error) {
189
+ // Fallback to Web Crypto API if available
190
+ if (typeof crypto.subtle !== 'undefined') {
191
+ try {
192
+ const publicKeyBuffer = Buffer.from(publicKeyHex, 'hex');
193
+ const publicKey = await crypto.subtle.importKey(
194
+ 'raw',
195
+ publicKeyBuffer,
196
+ {
197
+ name: 'Ed25519',
198
+ namedCurve: 'Ed25519',
199
+ },
200
+ false,
201
+ ['verify'],
202
+ );
203
+
204
+ return await crypto.subtle.verify(
205
+ 'Ed25519',
206
+ publicKey,
207
+ signature,
208
+ data,
209
+ );
210
+ } catch (webCryptoError) {
211
+ throw new Error(`Ed25519 verification failed: ${error.message}`);
212
+ }
213
+ }
214
+
215
+ throw new Error(`Ed25519 verification failed: ${error.message}`);
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Canonicalize manifest for consistent signing
221
+ * @param {object} manifest - Plugin manifest
222
+ * @returns {object} Canonicalized manifest
223
+ */
224
+ _canonicalizeManifest(manifest) {
225
+ // Remove signature-related fields and sort keys
226
+ const canonical = JSON.parse(JSON.stringify(manifest));
227
+ delete canonical.signature;
228
+ delete canonical.signerId;
229
+ delete canonical.signedAt;
230
+
231
+ // Sort object keys recursively for deterministic serialization
232
+ return this._sortObjectKeys(canonical);
233
+ }
234
+
235
+ /**
236
+ * Sort object keys recursively
237
+ * @param {any} obj - Object to sort
238
+ * @returns {any} Object with sorted keys
239
+ */
240
+ _sortObjectKeys(obj) {
241
+ if (obj === null || typeof obj !== 'object') {
242
+ return obj;
243
+ }
244
+
245
+ if (Array.isArray(obj)) {
246
+ return obj.map((item) => this._sortObjectKeys(item));
247
+ }
248
+
249
+ const sorted = {};
250
+ const keys = Object.keys(obj).sort();
251
+
252
+ for (const key of keys) {
253
+ sorted[key] = this._sortObjectKeys(obj[key]);
254
+ }
255
+
256
+ return sorted;
257
+ }
258
+
259
+ /**
260
+ * Hash manifest for audit trail
261
+ * @param {object} manifest - Canonicalized manifest
262
+ * @returns {string} SHA-256 hash
263
+ */
264
+ _hashManifest(manifest) {
265
+ const manifestData = JSON.stringify(manifest);
266
+ return crypto.createHash('sha256').update(manifestData).digest('hex');
267
+ }
268
+
269
+ /**
270
+ * Default audit logger
271
+ * @param {object} result - Verification result
272
+ * @param {object} manifest - Plugin manifest
273
+ */
274
+ _defaultAuditLogger(result, manifest) {
275
+ const auditEntry = {
276
+ timestamp: result.timestamp,
277
+ action: 'plugin_signature_verification',
278
+ pluginId: manifest.name || 'unknown',
279
+ pluginVersion: manifest.version || 'unknown',
280
+ signerId: result.signerId,
281
+ verified: result.verified,
282
+ error: result.error,
283
+ auditTrail: result.auditTrail,
284
+ };
285
+
286
+ // In production, this should write to secure audit log
287
+ if (process.env.NODE_ENV === 'production') {
288
+ console.warn('[AUDIT]', JSON.stringify(auditEntry));
289
+ } else {
290
+ console.log('[AUDIT]', auditEntry);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Audit verification attempt
296
+ * @param {object} result - Verification result
297
+ * @param {object} manifest - Plugin manifest
298
+ */
299
+ _auditVerification(result, manifest) {
300
+ try {
301
+ this.auditLogger(result, manifest);
302
+ } catch (error) {
303
+ // Audit logging failure should not break verification
304
+ console.error('Audit logging failed:', error.message);
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Add trusted signer
310
+ * @param {string} signerId - Signer identifier
311
+ * @param {string} publicKey - Ed25519 public key in hex
312
+ * @param {string} name - Human-readable name
313
+ * @param {object} metadata - Additional metadata
314
+ */
315
+ addTrustedSigner(signerId, publicKey, name, metadata = {}) {
316
+ if (!signerId || !publicKey || !name) {
317
+ throw new Error('signerId, publicKey, and name are required');
318
+ }
319
+
320
+ // Validate public key format
321
+ if (!/^[0-9a-fA-F]{64}$/.test(publicKey)) {
322
+ throw new Error('Public key must be 64 hex characters (32 bytes)');
323
+ }
324
+
325
+ this.trustStore.set(signerId, {
326
+ publicKey: publicKey.toLowerCase(),
327
+ name,
328
+ verified: metadata.verified || false,
329
+ addedAt: new Date().toISOString(),
330
+ ...metadata,
331
+ });
332
+
333
+ this.auditLogger(
334
+ {
335
+ timestamp: new Date().toISOString(),
336
+ action: 'trusted_signer_added',
337
+ signerId,
338
+ name,
339
+ verified: true,
340
+ auditTrail: [
341
+ {
342
+ action: 'signer_added',
343
+ signerId,
344
+ timestamp: new Date().toISOString(),
345
+ },
346
+ ],
347
+ },
348
+ { name: 'trust_store_update' },
349
+ );
350
+ }
351
+
352
+ /**
353
+ * Remove trusted signer
354
+ * @param {string} signerId - Signer identifier
355
+ */
356
+ removeTrustedSigner(signerId) {
357
+ if (!signerId) {
358
+ throw new Error('signerId is required');
359
+ }
360
+
361
+ const removed = this.trustStore.delete(signerId);
362
+
363
+ if (removed) {
364
+ this.auditLogger(
365
+ {
366
+ timestamp: new Date().toISOString(),
367
+ action: 'trusted_signer_removed',
368
+ signerId,
369
+ verified: true,
370
+ auditTrail: [
371
+ {
372
+ action: 'signer_removed',
373
+ signerId,
374
+ timestamp: new Date().toISOString(),
375
+ },
376
+ ],
377
+ },
378
+ { name: 'trust_store_update' },
379
+ );
380
+ }
381
+
382
+ return removed;
383
+ }
384
+
385
+ /**
386
+ * List trusted signers
387
+ * @returns {Array} List of trusted signers
388
+ */
389
+ listTrustedSigners() {
390
+ return Array.from(this.trustStore.entries()).map(([signerId, info]) => ({
391
+ signerId,
392
+ name: info.name,
393
+ verified: info.verified,
394
+ addedAt: info.addedAt,
395
+ }));
396
+ }
397
+ }
398
+
399
+ /**
400
+ * Create plugin signature verifier instance
401
+ * @param {object} options - Configuration options
402
+ * @returns {PluginSignatureVerifier} Verifier instance
403
+ */
404
+ function createPluginVerifier(options = {}) {
405
+ return new PluginSignatureVerifier(options);
406
+ }
407
+
408
+ module.exports = {
409
+ PluginSignatureVerifier,
410
+ createPluginVerifier,
411
+ };
412
+
413
+ // CommonJS/ESM interop
414
+ module.exports.default = module.exports;
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ function validateInput(input, opts = {}) {
4
+ const maxLength = opts.maxLength || 20000;
5
+ const inputStr = String(input);
6
+
7
+ const reasons = [];
8
+
9
+ // Check length
10
+ if (inputStr.length > maxLength) {
11
+ reasons.push(`Input exceeds maximum length of ${maxLength} characters`);
12
+ }
13
+
14
+ // Disallowed tokens (case-insensitive)
15
+ const disallowedTokens = ['```', '${', '</script', '--', ';', '||', '&&'];
16
+ const lowerInput = inputStr.toLowerCase();
17
+
18
+ for (const token of disallowedTokens) {
19
+ if (lowerInput.includes(token.toLowerCase())) {
20
+ reasons.push(`Input contains disallowed token: ${token}`);
21
+ }
22
+ }
23
+
24
+ const valid = reasons.length === 0;
25
+ const sanitized = inputStr.substring(0, maxLength);
26
+
27
+ return {
28
+ valid,
29
+ reasons,
30
+ sanitized,
31
+ };
32
+ }
33
+
34
+ // CJS+ESM interop pattern
35
+ module.exports = { validateInput };
36
+ module.exports.default = module.exports;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Version: 1.0.0
3
+ * Description: Enhanced CI diagnostics for plugin mock verification
4
+ * Author: Ali Kahwaji
5
+ */
6
+
7
+ const path = require('path');
8
+ // eslint-disable-line global-require
9
+ const chalk = require('chalk');
10
+ // eslint-disable-line global-require
11
+
12
+ /**
13
+ * Pretty prints CI validation errors and guidance
14
+ * @param {Array<{ file: string, missing: string[] }>} failures
15
+ */
16
+ function reportPluginValidationFailures(failures) {
17
+ if (!failures.length) return;
18
+
19
+ console.log(`\n${chalk.redBright.bold('❌ Plugin Mock Validation Failed')}`);
20
+ // eslint-disable-line no-console
21
+
22
+ failures.forEach(({ file, missing }) => {
23
+ const fileName = path.basename(file);
24
+ console.log(
25
+ // eslint-disable-line no-console
26
+ `${chalk.red(` ✖ ${fileName}`)} ${chalk.gray(`missing: ${missing.join(', ')}`)}`,
27
+ );
28
+
29
+ // Add method-level fix hints
30
+ missing.forEach((method) => {
31
+ const pluginType = fileName.split('.')[0].split('-')[1] || 'unknown';
32
+ console.error(
33
+ ` → Fix: implement method ${chalk.cyan(`${method}()`)} for plugin _type ${chalk.yellow(pluginType)}`,
34
+ );
35
+ // eslint-disable-line no-console
36
+ });
37
+ });
38
+
39
+ console.log(`\n${chalk.yellowBright('Recommended Fixes:')}`);
40
+ // eslint-disable-line no-console
41
+ console.log(
42
+ ' • Implement all required methods defined in /src/core/plugin-contracts.js',
43
+ );
44
+ // eslint-disable-line no-console
45
+ console.log(` • Re-run: ${chalk.cyan('npm run ci')} to confirm validation`);
46
+ // eslint-disable-line no-console
47
+ console.log(
48
+ ` • Optionally use: ${chalk.cyan('scripts/repair-fixtures.js')} to auto-repair mocks\n`,
49
+ );
50
+ // eslint-disable-line no-console
51
+ }
52
+
53
+ // Default export
54
+
55
+ module.exports = {
56
+ reportPluginValidationFailures,
57
+ };
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Version: 0.2.0
3
+ * Path: /src/utils/logger.js
4
+ * Description: Centralized structured logger with correlation IDs and enterprise features
5
+ * Author: Ali Kahwaji
6
+ */
7
+
8
+ const pino = require('pino'); // eslint-disable-line global-require
9
+ const crypto = require('crypto'); // eslint-disable-line global-require
10
+
11
+ // Determine log level with environment override
12
+ const getLogLevel = () => {
13
+ if (process.env.LOG_LEVEL) {
14
+ const level = process.env.LOG_LEVEL.toLowerCase();
15
+ const validLevels = [
16
+ 'trace',
17
+ 'debug',
18
+ 'info',
19
+ 'warn',
20
+ 'error',
21
+ 'fatal',
22
+ 'silent',
23
+ ];
24
+ if (validLevels.includes(level)) {
25
+ return level;
26
+ }
27
+ }
28
+ return process.env.NODE_ENV === 'test' ? 'silent' : 'info';
29
+ };
30
+
31
+ // Determine if pretty printing should be enabled
32
+ const shouldUsePrettyPrint = () => {
33
+ // Enable pretty print in development or when explicitly requested
34
+ return (
35
+ process.env.NODE_ENV === 'development' || process.env.LOG_PRETTY === 'true'
36
+ );
37
+ };
38
+
39
+ /**
40
+ * Create a structured JSON logger instance with correlation IDs and enterprise features.
41
+ * Supports distributed tracing, tenant isolation, and security audit logging.
42
+ */
43
+ const baseLogger = pino({
44
+ name: 'rag-pipeline-utils',
45
+ level: getLogLevel(),
46
+ formatters: {
47
+ level: (label) => {
48
+ return { level: label };
49
+ },
50
+ log: (object) => {
51
+ // Add correlation ID if available
52
+ if (object.correlationId) {
53
+ object.correlationId = object.correlationId;
54
+ }
55
+ return object;
56
+ },
57
+ },
58
+ // Pretty print in development or when LOG_PRETTY=true
59
+ ...(shouldUsePrettyPrint() && {
60
+ transport: {
61
+ target: 'pino-pretty',
62
+ options: {
63
+ colorize: true,
64
+ translateTime: 'HH:MM:ss',
65
+ ignore: 'pid,hostname',
66
+ },
67
+ },
68
+ }),
69
+ });
70
+
71
+ /**
72
+ * Enhanced logger with correlation ID support and structured context
73
+ */
74
+ class StructuredLogger {
75
+ constructor(baseLogger) {
76
+ this.logger = baseLogger;
77
+ this.correlationId = null;
78
+ this.context = {};
79
+ }
80
+
81
+ /**
82
+ * Create a child logger with correlation ID and context
83
+ */
84
+ child(context = {}) {
85
+ const correlationId = context.correlationId || crypto.randomUUID();
86
+ const childLogger = new StructuredLogger(
87
+ this.logger.child({
88
+ correlationId,
89
+ ...context,
90
+ }),
91
+ );
92
+ childLogger.correlationId = correlationId;
93
+ childLogger.context = { ...this.context, ...context };
94
+ return childLogger;
95
+ }
96
+
97
+ /**
98
+ * Set correlation ID for distributed tracing
99
+ */
100
+ setCorrelationId(correlationId) {
101
+ this.correlationId = correlationId;
102
+ return this;
103
+ }
104
+
105
+ /**
106
+ * Add persistent context to all log messages
107
+ */
108
+ setContext(context) {
109
+ this.context = { ...this.context, ...context };
110
+ return this;
111
+ }
112
+
113
+ /**
114
+ * Log with automatic correlation ID and context injection
115
+ */
116
+ _log(level, message, meta = {}) {
117
+ const logData = {
118
+ ...this.context,
119
+ correlationId: this.correlationId || crypto.randomUUID(),
120
+ timestamp: new Date().toISOString(),
121
+ ...meta,
122
+ };
123
+
124
+ this.logger[level](logData, message);
125
+ }
126
+
127
+ info(message, meta) {
128
+ this._log('info', message, meta);
129
+ }
130
+
131
+ error(message, meta) {
132
+ this._log('error', message, meta);
133
+ }
134
+
135
+ warn(message, meta) {
136
+ this._log('warn', message, meta);
137
+ }
138
+
139
+ debug(message, meta) {
140
+ this._log('debug', message, meta);
141
+ }
142
+
143
+ /**
144
+ * Security audit logging with enhanced metadata
145
+ */
146
+ audit(action, meta = {}) {
147
+ this._log('info', `AUDIT: ${action}`, {
148
+ ...meta,
149
+ audit: true,
150
+ action,
151
+ severity: meta.severity || 'medium',
152
+ });
153
+ }
154
+
155
+ /**
156
+ * Performance logging with timing information
157
+ */
158
+ performance(operation, duration, meta = {}) {
159
+ this._log('info', `PERFORMANCE: ${operation}`, {
160
+ ...meta,
161
+ performance: true,
162
+ operation,
163
+ duration,
164
+ unit: 'ms',
165
+ });
166
+ }
167
+
168
+ /**
169
+ * Tenant-specific logging for multi-tenant applications
170
+ */
171
+ tenant(tenantId, message, meta = {}) {
172
+ this._log('info', message, {
173
+ ...meta,
174
+ tenantId,
175
+ tenant: true,
176
+ });
177
+ }
178
+ }
179
+
180
+ // Create default logger instance
181
+ const logger = new StructuredLogger(baseLogger);
182
+
183
+ /**
184
+ * Create a logger with correlation ID for request tracing
185
+ */
186
+ function createLogger(correlationId, context = {}) {
187
+ return logger.child({ correlationId, ...context });
188
+ }
189
+
190
+ /**
191
+ * Middleware function for Express.js to add correlation IDs to requests
192
+ */
193
+ function correlationMiddleware(req, res, next) {
194
+ const correlationId = req.headers['x-correlation-id'] || crypto.randomUUID();
195
+ req.correlationId = correlationId;
196
+ req.logger = createLogger(correlationId, {
197
+ method: req.method,
198
+ url: req.url,
199
+ userAgent: req.headers['user-agent'],
200
+ });
201
+
202
+ res.setHeader('x-correlation-id', correlationId);
203
+ next();
204
+ }
205
+
206
+ module.exports = {
207
+ logger,
208
+ createLogger,
209
+ correlationMiddleware,
210
+ StructuredLogger,
211
+ };