@kabran-tecnologia/kabran-config 1.6.0 → 1.8.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,421 @@
1
+ /**
2
+ * Telemetry Configuration Defaults
3
+ *
4
+ * Smart defaults that work for most Kabran projects.
5
+ * All values can be overridden via environment variables.
6
+ *
7
+ * @module telemetry/config/defaults
8
+ */
9
+
10
+ // =============================================================================
11
+ // Environment Variable Helpers
12
+ // =============================================================================
13
+
14
+ /**
15
+ * Get environment variable with fallback
16
+ * @param {string} key - Environment variable key
17
+ * @param {string} fallback - Fallback value
18
+ * @returns {string}
19
+ */
20
+ function getEnv(key, fallback) {
21
+ if (typeof process !== 'undefined' && process.env) {
22
+ return process.env[key] || fallback
23
+ }
24
+ if (typeof Deno !== 'undefined') {
25
+ return Deno.env.get(key) || fallback
26
+ }
27
+ return fallback
28
+ }
29
+
30
+ /**
31
+ * Get numeric environment variable with fallback
32
+ * @param {string} key - Environment variable key
33
+ * @param {number} fallback - Fallback value
34
+ * @returns {number}
35
+ */
36
+ function getEnvNumber(key, fallback) {
37
+ const value = getEnv(key, '')
38
+ if (value === '') return fallback
39
+ const parsed = parseInt(value, 10)
40
+ return isNaN(parsed) ? fallback : parsed
41
+ }
42
+
43
+ /**
44
+ * Parse CORS URLs from environment variable
45
+ * @param {string} value - Comma-separated URL patterns
46
+ * @returns {RegExp[]}
47
+ */
48
+ function parseCorsUrls(value) {
49
+ if (!value) return null
50
+ return value.split(',').map((pattern) => new RegExp(pattern.trim()))
51
+ }
52
+
53
+ /**
54
+ * Parse event names from environment variable
55
+ * @param {string} value - Comma-separated event names
56
+ * @returns {string[]}
57
+ */
58
+ function parseEventNames(value) {
59
+ if (!value) return null
60
+ return value.split(',').map((name) => name.trim())
61
+ }
62
+
63
+ /**
64
+ * Parse ignore paths from environment variable
65
+ * @param {string} value - Comma-separated paths
66
+ * @returns {string[]}
67
+ */
68
+ function parseIgnorePaths(value) {
69
+ if (!value) return null
70
+ return value.split(',').map((path) => path.trim())
71
+ }
72
+
73
+ // =============================================================================
74
+ // Default Constants
75
+ // =============================================================================
76
+
77
+ /**
78
+ * Default OTLP endpoint
79
+ * Override: OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_ENDPOINT
80
+ */
81
+ export const DEFAULT_ENDPOINT = 'https://otel.kabran.com.br'
82
+
83
+ /**
84
+ * Default OTLP traces path
85
+ * Override: OTEL_EXPORTER_OTLP_TRACES_PATH
86
+ */
87
+ export const DEFAULT_TRACES_PATH = '/v1/traces'
88
+
89
+ /**
90
+ * Default sampling rate (10%)
91
+ * Override: OTEL_SAMPLE_RATE
92
+ */
93
+ export const DEFAULT_SAMPLE_RATE = 0.1
94
+
95
+ /**
96
+ * Default service version
97
+ * Override: SERVICE_VERSION
98
+ */
99
+ export const DEFAULT_SERVICE_VERSION = '1.0.0'
100
+
101
+ /**
102
+ * Default namespace
103
+ * Override: OTEL_NAMESPACE or SERVICE_NAMESPACE
104
+ */
105
+ export const DEFAULT_NAMESPACE = 'kabran'
106
+
107
+ /**
108
+ * Default CORS URLs for trace header propagation
109
+ * Override: OTEL_PROPAGATE_TRACE_HEADER_CORS_URLS (comma-separated regex patterns)
110
+ */
111
+ export const DEFAULT_CORS_URLS = [
112
+ /.*\.supabase\.co/,
113
+ /.*\.kabran\.com\.br/,
114
+ /localhost/,
115
+ ]
116
+
117
+ /**
118
+ * Default instrumentation options
119
+ * Override: OTEL_INSTRUMENTATION_FETCH, OTEL_INSTRUMENTATION_DOCUMENT_LOAD, etc.
120
+ */
121
+ export const DEFAULT_INSTRUMENTATION = {
122
+ fetch: true,
123
+ documentLoad: true,
124
+ userInteraction: true,
125
+ database: true,
126
+ }
127
+
128
+ /**
129
+ * Default user interaction events
130
+ * Override: OTEL_USER_INTERACTION_EVENTS (comma-separated)
131
+ */
132
+ export const DEFAULT_USER_INTERACTION_EVENTS = ['click', 'submit']
133
+
134
+ /**
135
+ * Default paths to ignore in middleware
136
+ * Override: OTEL_IGNORE_PATHS (comma-separated)
137
+ */
138
+ export const DEFAULT_IGNORE_PATHS = ['/health', '/ready', '/metrics']
139
+
140
+ // =============================================================================
141
+ // Exporter Configuration
142
+ // =============================================================================
143
+
144
+ /**
145
+ * Default OTLP export timeout (ms) for Node.js
146
+ * Override: OTEL_EXPORTER_OTLP_TIMEOUT
147
+ */
148
+ export const DEFAULT_EXPORT_TIMEOUT_NODE = 10000
149
+
150
+ /**
151
+ * Default OTLP export timeout (ms) for Edge/Serverless
152
+ * Override: OTEL_EXPORTER_OTLP_TIMEOUT_EDGE
153
+ */
154
+ export const DEFAULT_EXPORT_TIMEOUT_EDGE = 5000
155
+
156
+ // =============================================================================
157
+ // Batch Span Processor Configuration - Node.js
158
+ // =============================================================================
159
+
160
+ /**
161
+ * Default max queue size for Node.js BatchSpanProcessor
162
+ * Override: OTEL_BSP_MAX_QUEUE_SIZE
163
+ */
164
+ export const DEFAULT_BSP_MAX_QUEUE_SIZE_NODE = 2048
165
+
166
+ /**
167
+ * Default max export batch size for Node.js BatchSpanProcessor
168
+ * Override: OTEL_BSP_MAX_EXPORT_BATCH_SIZE
169
+ */
170
+ export const DEFAULT_BSP_MAX_EXPORT_BATCH_SIZE_NODE = 512
171
+
172
+ /**
173
+ * Default scheduled delay (ms) for Node.js BatchSpanProcessor
174
+ * Override: OTEL_BSP_SCHEDULE_DELAY
175
+ */
176
+ export const DEFAULT_BSP_SCHEDULE_DELAY_NODE = 5000
177
+
178
+ // =============================================================================
179
+ // Batch Span Processor Configuration - Frontend
180
+ // =============================================================================
181
+
182
+ /**
183
+ * Default max queue size for Frontend BatchSpanProcessor
184
+ * Override: OTEL_BSP_MAX_QUEUE_SIZE_FRONTEND
185
+ */
186
+ export const DEFAULT_BSP_MAX_QUEUE_SIZE_FRONTEND = 100
187
+
188
+ /**
189
+ * Default max export batch size for Frontend BatchSpanProcessor
190
+ * Override: OTEL_BSP_MAX_EXPORT_BATCH_SIZE_FRONTEND
191
+ */
192
+ export const DEFAULT_BSP_MAX_EXPORT_BATCH_SIZE_FRONTEND = 10
193
+
194
+ /**
195
+ * Default scheduled delay (ms) for Frontend BatchSpanProcessor
196
+ * Override: OTEL_BSP_SCHEDULE_DELAY_FRONTEND
197
+ */
198
+ export const DEFAULT_BSP_SCHEDULE_DELAY_FRONTEND = 500
199
+
200
+ // =============================================================================
201
+ // Tracer Names (fallbacks)
202
+ // =============================================================================
203
+
204
+ /**
205
+ * Default tracer name for Node.js
206
+ */
207
+ export const DEFAULT_TRACER_NAME_NODE = 'kabran-node'
208
+
209
+ /**
210
+ * Default tracer name for Frontend
211
+ */
212
+ export const DEFAULT_TRACER_NAME_FRONTEND = 'kabran-frontend'
213
+
214
+ /**
215
+ * Default tracer name for Edge
216
+ */
217
+ export const DEFAULT_TRACER_NAME_EDGE = 'kabran-edge'
218
+
219
+ // =============================================================================
220
+ // Logger Configuration
221
+ // =============================================================================
222
+
223
+ /**
224
+ * Default trace ID display length in logs
225
+ * Override: OTEL_LOG_TRACE_ID_LENGTH
226
+ */
227
+ export const DEFAULT_LOG_TRACE_ID_LENGTH = 8
228
+
229
+ /**
230
+ * Check if colors should be disabled
231
+ * Override: NO_COLOR or FORCE_COLOR=false
232
+ */
233
+ export function shouldDisableColors() {
234
+ if (typeof process !== 'undefined' && process.env) {
235
+ if (process.env.NO_COLOR !== undefined) return true
236
+ if (process.env.FORCE_COLOR === 'false' || process.env.FORCE_COLOR === '0') return true
237
+ }
238
+ return false
239
+ }
240
+
241
+ // =============================================================================
242
+ // Error Response Configuration (Edge)
243
+ // =============================================================================
244
+
245
+ /**
246
+ * Default error message for unhandled errors
247
+ * Override: OTEL_ERROR_MESSAGE
248
+ */
249
+ export const DEFAULT_ERROR_MESSAGE = 'Internal server error'
250
+
251
+ /**
252
+ * Default error code for unhandled errors
253
+ * Override: OTEL_ERROR_CODE
254
+ */
255
+ export const DEFAULT_ERROR_CODE = 'INTERNAL_ERROR'
256
+
257
+ // =============================================================================
258
+ // Runtime Configuration Getters
259
+ // =============================================================================
260
+
261
+ /**
262
+ * Get configured traces path
263
+ * @returns {string}
264
+ */
265
+ export function getTracesPath() {
266
+ return getEnv('OTEL_EXPORTER_OTLP_TRACES_PATH', DEFAULT_TRACES_PATH)
267
+ }
268
+
269
+ /**
270
+ * Get configured namespace
271
+ * @returns {string}
272
+ */
273
+ export function getNamespace() {
274
+ return getEnv('OTEL_NAMESPACE', getEnv('SERVICE_NAMESPACE', DEFAULT_NAMESPACE))
275
+ }
276
+
277
+ /**
278
+ * Get configured CORS URLs
279
+ * @returns {RegExp[]}
280
+ */
281
+ export function getCorsUrls() {
282
+ const envValue = getEnv('OTEL_PROPAGATE_TRACE_HEADER_CORS_URLS', '')
283
+ return parseCorsUrls(envValue) || DEFAULT_CORS_URLS
284
+ }
285
+
286
+ /**
287
+ * Get configured user interaction events
288
+ * @returns {string[]}
289
+ */
290
+ export function getUserInteractionEvents() {
291
+ const envValue = getEnv('OTEL_USER_INTERACTION_EVENTS', '')
292
+ return parseEventNames(envValue) || DEFAULT_USER_INTERACTION_EVENTS
293
+ }
294
+
295
+ /**
296
+ * Get configured ignore paths for middleware
297
+ * @returns {string[]}
298
+ */
299
+ export function getIgnorePaths() {
300
+ const envValue = getEnv('OTEL_IGNORE_PATHS', '')
301
+ return parseIgnorePaths(envValue) || DEFAULT_IGNORE_PATHS
302
+ }
303
+
304
+ /**
305
+ * Get export timeout for Node.js
306
+ * @returns {number}
307
+ */
308
+ export function getExportTimeoutNode() {
309
+ return getEnvNumber('OTEL_EXPORTER_OTLP_TIMEOUT', DEFAULT_EXPORT_TIMEOUT_NODE)
310
+ }
311
+
312
+ /**
313
+ * Get export timeout for Edge
314
+ * @returns {number}
315
+ */
316
+ export function getExportTimeoutEdge() {
317
+ return getEnvNumber('OTEL_EXPORTER_OTLP_TIMEOUT_EDGE',
318
+ getEnvNumber('OTEL_EXPORTER_OTLP_TIMEOUT', DEFAULT_EXPORT_TIMEOUT_EDGE))
319
+ }
320
+
321
+ /**
322
+ * Get BatchSpanProcessor config for Node.js
323
+ * @returns {{ maxQueueSize: number, maxExportBatchSize: number, scheduledDelayMillis: number }}
324
+ */
325
+ export function getBspConfigNode() {
326
+ return {
327
+ maxQueueSize: getEnvNumber('OTEL_BSP_MAX_QUEUE_SIZE', DEFAULT_BSP_MAX_QUEUE_SIZE_NODE),
328
+ maxExportBatchSize: getEnvNumber('OTEL_BSP_MAX_EXPORT_BATCH_SIZE', DEFAULT_BSP_MAX_EXPORT_BATCH_SIZE_NODE),
329
+ scheduledDelayMillis: getEnvNumber('OTEL_BSP_SCHEDULE_DELAY', DEFAULT_BSP_SCHEDULE_DELAY_NODE),
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Get BatchSpanProcessor config for Frontend
335
+ * @returns {{ maxQueueSize: number, maxExportBatchSize: number, scheduledDelayMillis: number }}
336
+ */
337
+ export function getBspConfigFrontend() {
338
+ return {
339
+ maxQueueSize: getEnvNumber('OTEL_BSP_MAX_QUEUE_SIZE_FRONTEND', DEFAULT_BSP_MAX_QUEUE_SIZE_FRONTEND),
340
+ maxExportBatchSize: getEnvNumber('OTEL_BSP_MAX_EXPORT_BATCH_SIZE_FRONTEND', DEFAULT_BSP_MAX_EXPORT_BATCH_SIZE_FRONTEND),
341
+ scheduledDelayMillis: getEnvNumber('OTEL_BSP_SCHEDULE_DELAY_FRONTEND', DEFAULT_BSP_SCHEDULE_DELAY_FRONTEND),
342
+ }
343
+ }
344
+
345
+ /**
346
+ * Get error response config for Edge
347
+ * @returns {{ message: string, code: string }}
348
+ */
349
+ export function getErrorResponseConfig() {
350
+ return {
351
+ message: getEnv('OTEL_ERROR_MESSAGE', DEFAULT_ERROR_MESSAGE),
352
+ code: getEnv('OTEL_ERROR_CODE', DEFAULT_ERROR_CODE),
353
+ }
354
+ }
355
+
356
+ /**
357
+ * Get trace ID display length for logs
358
+ * @returns {number}
359
+ */
360
+ export function getLogTraceIdLength() {
361
+ return getEnvNumber('OTEL_LOG_TRACE_ID_LENGTH', DEFAULT_LOG_TRACE_ID_LENGTH)
362
+ }
363
+
364
+ /**
365
+ * Detect if telemetry should be enabled based on environment
366
+ *
367
+ * Rules:
368
+ * - Explicit setting via OTEL_ENABLED/VITE_OTEL_ENABLED takes precedence
369
+ * - Production: enabled by default
370
+ * - Development/Test: disabled by default
371
+ *
372
+ * @param {string|undefined} explicitSetting - Explicit enable setting
373
+ * @param {string} mode - Environment mode (production, development, test)
374
+ * @returns {boolean} Whether telemetry should be enabled
375
+ */
376
+ export function detectEnabled(explicitSetting, mode) {
377
+ if (explicitSetting !== undefined) {
378
+ return explicitSetting === 'true'
379
+ }
380
+ return mode === 'production'
381
+ }
382
+
383
+ /**
384
+ * Detect environment from various sources
385
+ *
386
+ * @returns {string} Environment name
387
+ */
388
+ export function detectEnvironment() {
389
+ // Node.js
390
+ if (typeof process !== 'undefined' && process.env) {
391
+ return process.env.ENVIRONMENT || process.env.NODE_ENV || 'development'
392
+ }
393
+
394
+ // Deno
395
+ if (typeof Deno !== 'undefined') {
396
+ return Deno.env.get('ENVIRONMENT') || 'production'
397
+ }
398
+
399
+ return 'development'
400
+ }
401
+
402
+ /**
403
+ * Get default configuration
404
+ *
405
+ * @param {string} serviceName - Service name
406
+ * @returns {import('./types').ResolvedTelemetryConfig} Default configuration
407
+ */
408
+ export function getDefaults(serviceName) {
409
+ return {
410
+ serviceName,
411
+ serviceVersion: DEFAULT_SERVICE_VERSION,
412
+ environment: detectEnvironment(),
413
+ endpoint: DEFAULT_ENDPOINT,
414
+ sampleRate: DEFAULT_SAMPLE_RATE,
415
+ enabled: false, // Will be resolved by detectEnabled()
416
+ namespace: DEFAULT_NAMESPACE,
417
+ resourceAttributes: {},
418
+ propagateTraceHeaderCorsUrls: DEFAULT_CORS_URLS,
419
+ instrumentation: { ...DEFAULT_INSTRUMENTATION },
420
+ }
421
+ }
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Telemetry Configuration Module
3
+ *
4
+ * Provides configuration management for telemetry with:
5
+ * - Smart defaults for Kabran projects
6
+ * - Environment variable support
7
+ * - Config file support
8
+ * - Validation
9
+ *
10
+ * @module telemetry/config
11
+ */
12
+
13
+ import {
14
+ DEFAULT_ENDPOINT,
15
+ DEFAULT_SAMPLE_RATE,
16
+ DEFAULT_SERVICE_VERSION,
17
+ DEFAULT_NAMESPACE,
18
+ DEFAULT_CORS_URLS,
19
+ DEFAULT_INSTRUMENTATION,
20
+ detectEnabled,
21
+ detectEnvironment,
22
+ getDefaults,
23
+ } from './defaults.mjs'
24
+
25
+ /**
26
+ * Define telemetry configuration with type checking and defaults
27
+ *
28
+ * @param {import('../shared/types').TelemetryConfig} config - Configuration options
29
+ * @returns {import('../shared/types').TelemetryConfig} Validated configuration
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * import { defineTelemetryConfig } from '@kabran-tecnologia/kabran-config/telemetry/config'
34
+ *
35
+ * export default defineTelemetryConfig({
36
+ * serviceName: 'my-app',
37
+ * sampleRate: 0.5,
38
+ * })
39
+ * ```
40
+ */
41
+ export function defineTelemetryConfig(config) {
42
+ if (!config.serviceName) {
43
+ throw new Error('[Telemetry] serviceName is required')
44
+ }
45
+
46
+ return config
47
+ }
48
+
49
+ /**
50
+ * Resolve configuration by merging:
51
+ * 1. Explicit config (highest priority)
52
+ * 2. Environment variables
53
+ * 3. Defaults (lowest priority)
54
+ *
55
+ * @param {import('../shared/types').TelemetryConfig} config - User configuration
56
+ * @param {Object} env - Environment variables object
57
+ * @param {string} mode - Environment mode (production, development, test)
58
+ * @returns {import('../shared/types').ResolvedTelemetryConfig} Resolved configuration
59
+ */
60
+ export function resolveConfig(config, env = {}, mode = 'development') {
61
+ const defaults = getDefaults(config.serviceName)
62
+
63
+ // Resolve from environment variables (frontend style: VITE_*)
64
+ const envEndpoint = env.VITE_OTEL_ENDPOINT || env.OTEL_ENDPOINT
65
+ const envServiceName = env.VITE_SERVICE_NAME || env.SERVICE_NAME
66
+ const envServiceVersion = env.VITE_SERVICE_VERSION || env.SERVICE_VERSION
67
+ const envEnvironment = env.VITE_ENVIRONMENT || env.ENVIRONMENT || env.NODE_ENV
68
+ const envSampleRate = env.VITE_OTEL_SAMPLE_RATE || env.OTEL_SAMPLE_RATE
69
+ const envEnabled = env.VITE_OTEL_ENABLED || env.OTEL_ENABLED
70
+
71
+ // Merge configuration
72
+ const resolved = {
73
+ serviceName: config.serviceName || envServiceName || defaults.serviceName,
74
+ serviceVersion: config.serviceVersion || envServiceVersion || defaults.serviceVersion,
75
+ environment: config.environment || envEnvironment || defaults.environment,
76
+ endpoint: config.endpoint || envEndpoint || defaults.endpoint,
77
+ sampleRate: config.sampleRate ?? (envSampleRate ? parseFloat(envSampleRate) : defaults.sampleRate),
78
+ enabled: config.enabled ?? detectEnabled(envEnabled, mode),
79
+ namespace: config.namespace || defaults.namespace,
80
+ resourceAttributes: {
81
+ ...defaults.resourceAttributes,
82
+ ...config.resourceAttributes,
83
+ },
84
+ propagateTraceHeaderCorsUrls: config.propagateTraceHeaderCorsUrls || defaults.propagateTraceHeaderCorsUrls,
85
+ instrumentation: {
86
+ ...defaults.instrumentation,
87
+ ...config.instrumentation,
88
+ },
89
+ }
90
+
91
+ return resolved
92
+ }
93
+
94
+ /**
95
+ * Validate configuration
96
+ *
97
+ * @param {import('../shared/types').ResolvedTelemetryConfig} config - Configuration to validate
98
+ * @returns {{ valid: boolean, errors: string[] }} Validation result
99
+ */
100
+ export function validateConfig(config) {
101
+ const errors = []
102
+
103
+ if (!config.serviceName) {
104
+ errors.push('serviceName is required')
105
+ }
106
+
107
+ if (config.sampleRate < 0 || config.sampleRate > 1) {
108
+ errors.push('sampleRate must be between 0 and 1')
109
+ }
110
+
111
+ if (config.endpoint && !config.endpoint.startsWith('http')) {
112
+ errors.push('endpoint must be a valid URL')
113
+ }
114
+
115
+ return {
116
+ valid: errors.length === 0,
117
+ errors,
118
+ }
119
+ }
120
+
121
+ // Re-export defaults for convenience
122
+ export {
123
+ DEFAULT_ENDPOINT,
124
+ DEFAULT_SAMPLE_RATE,
125
+ DEFAULT_SERVICE_VERSION,
126
+ DEFAULT_NAMESPACE,
127
+ DEFAULT_CORS_URLS,
128
+ DEFAULT_INSTRUMENTATION,
129
+ detectEnabled,
130
+ detectEnvironment,
131
+ getDefaults,
132
+ }