@ciq-dev/neoiq-foundation-node 1.0.0 → 1.0.1-beta.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.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
- # @commerceiq/neoiq-foundation-node
1
+ # @ciq-dev/neoiq-foundation-node
2
2
 
3
3
  Node.js observability foundation for CommerceIQ services. Integrates with **Groundcover** via **OpenTelemetry**.
4
4
 
5
5
  ```
6
6
  ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
7
- Your Service │────▶│ OTEL Collector │────▶│ Groundcover │
7
+ Our Service │────▶│ OTEL Collector │────▶│ Groundcover │
8
8
  │ (auth-service) │ │ (in-cluster) │ │ (dashboard) │
9
9
  └─────────────────┘ └─────────────────┘ └─────────────────┘
10
10
  │ │
@@ -20,42 +20,50 @@ Node.js observability foundation for CommerceIQ services. Integrates with **Grou
20
20
  - **Traces**: Automatic span creation for HTTP requests (incoming & outgoing)
21
21
  - **Metrics**: HTTP request counts, durations, errors + custom business metrics
22
22
  - **Logs**: Structured JSON logs with automatic trace context (traceId, spanId, correlationId)
23
+ - **Selective Enablement**: Enable only the features you need
24
+ - **Type-Safe Configuration**: Zod validation with helpful error messages
23
25
  - **Fastify Plugin**: One-line integration for request lifecycle
24
26
  - **HTTP Client**: Axios wrapper with retry, circuit breaker, and trace propagation
25
27
 
28
+ ---
29
+
26
30
  ## Quick Start
27
31
 
28
32
  ### 1. Install
29
33
 
30
34
  ```bash
31
- npm install @commerceiq/neoiq-foundation-node
35
+ npm install @ciq-dev/neoiq-foundation-node
32
36
 
33
37
  # Peer dependencies
34
- npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-metrics \
38
+ npm install zod @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-metrics \
35
39
  @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc \
36
40
  @opentelemetry/auto-instrumentations-node @opentelemetry/resources @opentelemetry/semantic-conventions \
37
41
  pino pino-pretty axios axios-retry opossum fastify-plugin
38
42
  ```
39
43
 
40
- ### 2. Initialize (First Line of Your App)
44
+ ### 2. Initialize (First Line of the App)
41
45
 
42
46
  ```typescript
43
- import { init, logger, observabilityPlugin } from '@commerceiq/neoiq-foundation-node';
47
+ import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
44
48
 
45
- // Must be called BEFORE other imports
46
- init({
47
- serviceName: 'neoiq-auth-service',
49
+ // Create foundation instance (must be called BEFORE other imports)
50
+ const foundation = createFoundation({
51
+ serviceName: 'auth-service',
48
52
  serviceVersion: '1.0.0',
49
53
  });
54
+
55
+ // Export for use in other files
56
+ export { foundation };
50
57
  ```
51
58
 
52
59
  ### 3. Add Fastify Plugin
53
60
 
54
61
  ```typescript
55
62
  import Fastify from 'fastify';
63
+ import { foundation } from './foundation';
56
64
 
57
65
  const app = Fastify();
58
- app.register(observabilityPlugin, { serviceName: 'neoiq-auth-service' });
66
+ app.register(foundation.fastifyPlugin);
59
67
  ```
60
68
 
61
69
  ### 4. Set Environment Variable in Kubernetes
@@ -66,36 +74,153 @@ env:
66
74
  value: 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317'
67
75
  ```
68
76
 
69
- That's it! Your traces and metrics will flow to Groundcover.
77
+ That's it! Traces and metrics will flow to Groundcover.
78
+
79
+ ---
80
+
81
+ ## Selective Feature Enablement
82
+
83
+ Enable only the features you need for lightweight deployments:
84
+
85
+ ```typescript
86
+ import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
87
+
88
+ // Full observability (default)
89
+ const foundation = createFoundation({
90
+ serviceName: 'auth-service',
91
+ });
92
+
93
+ // Logging only (lightweight mode)
94
+ const foundation = createFoundation({
95
+ serviceName: 'simple-worker',
96
+ features: {
97
+ tracing: false,
98
+ metrics: false,
99
+ logging: true,
100
+ },
101
+ });
102
+
103
+ // Custom auto-instrumentation
104
+ const foundation = createFoundation({
105
+ serviceName: 'api-gateway',
106
+ features: {
107
+ tracing: true,
108
+ metrics: true,
109
+ autoInstrumentation: {
110
+ http: true,
111
+ fastify: true,
112
+ mongodb: false, // Not using MongoDB
113
+ redis: false, // Not using Redis
114
+ pg: true, // Using PostgreSQL
115
+ },
116
+ },
117
+ });
118
+ ```
70
119
 
71
120
  ---
72
121
 
73
122
  ## API Reference
74
123
 
75
- ### `init(options)`
124
+ ### `createFoundation(options)`
76
125
 
77
- Initialize OpenTelemetry. **Must be called before other imports.**
126
+ Create a foundation instance with full observability capabilities.
78
127
 
79
128
  ```typescript
80
- init({
81
- serviceName: 'my-service', // Required
82
- serviceVersion: '1.0.0', // Default: '1.0.0'
83
- environment: 'production', // Default: 'development'
84
- otlpEndpoint: 'http://...:4317', // Default: cluster OTEL Collector
85
- logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error'
86
- metricsIntervalMs: 5000, // Default: 5000 (5 seconds)
129
+ import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
130
+
131
+ const foundation = createFoundation({
132
+ // Required
133
+ serviceName: 'my-service',
134
+
135
+ // Optional
136
+ serviceVersion: '1.0.0', // Default: '1.0.0' or $SERVICE_VERSION
137
+ environment: 'production', // Default: 'development' or $NODE_ENV
138
+
139
+ // Feature toggles
140
+ features: {
141
+ tracing: true, // Enable distributed tracing
142
+ metrics: true, // Enable metrics collection
143
+ logging: true, // Enable structured logging
144
+ autoInstrumentation: { // Fine-grained control
145
+ http: true,
146
+ fastify: true,
147
+ express: true,
148
+ mongodb: true,
149
+ pg: true,
150
+ mysql: true,
151
+ redis: true,
152
+ ioredis: true,
153
+ fs: false, // Disabled by default (noisy)
154
+ dns: false, // Disabled by default
155
+ },
156
+ },
157
+
158
+ // OTEL configuration
159
+ otel: {
160
+ endpoint: 'http://...:4317', // Default: cluster OTEL Collector
161
+ metricsIntervalMs: 5000, // Default: 5000 (5 seconds)
162
+ traceSampleRate: 1.0, // Default: 1.0 (100%)
163
+ },
164
+
165
+ // Logging configuration
166
+ logging: {
167
+ level: 'info', // 'debug' | 'info' | 'warn' | 'error'
168
+ prettyPrint: false, // Auto-detect from environment
169
+ },
87
170
  });
88
171
  ```
89
172
 
90
- ### `logger`
173
+ ### Foundation Instance
91
174
 
92
- Structured logger with automatic trace context injection.
175
+ The foundation instance provides access to all observability features:
93
176
 
94
177
  ```typescript
95
- import { logger } from '@commerceiq/neoiq-foundation-node';
178
+ // Logging
179
+ foundation.logger.info({ userId: '123' }, 'User logged in');
180
+ foundation.logger.error({ error: err.message }, 'Failed to process');
181
+
182
+ // Metrics
183
+ const meter = foundation.getMeter('my-service');
184
+ const counter = meter.createCounter('logins.total');
185
+ counter.add(1, { provider: 'workos' });
186
+
187
+ // Tracing
188
+ const tracer = foundation.getTracer();
189
+ tracer.startActiveSpan('validateToken', (span) => {
190
+ // ... do work
191
+ span.end();
192
+ });
193
+
194
+ // HTTP Client
195
+ const client = foundation.createHttpClient({
196
+ baseURL: 'http://user-service:3000',
197
+ serviceName: 'user-service',
198
+ });
96
199
 
97
- logger.info({ userId: '123' }, 'User logged in');
98
- logger.error({ error: err.message }, 'Failed to process');
200
+ // Fastify Plugin
201
+ app.register(foundation.fastifyPlugin);
202
+
203
+ // Shutdown
204
+ await foundation.shutdown();
205
+
206
+ // Check features
207
+ console.log(foundation.features);
208
+ // { tracing: true, metrics: true, logging: true }
209
+ ```
210
+
211
+ ### `foundation.logger`
212
+
213
+ Structured logger with automatic trace context injection.
214
+
215
+ ```typescript
216
+ foundation.logger.info({ userId: '123' }, 'User logged in');
217
+ foundation.logger.error({ error: err.message }, 'Failed to process');
218
+ foundation.logger.warn({ attempt: 3 }, 'Retry attempt');
219
+ foundation.logger.debug({ payload: data }, 'Processing request');
220
+
221
+ // Child logger for component-specific logging
222
+ const authLogger = foundation.logger.child({ component: 'auth' });
223
+ authLogger.info({}, 'Auth module initialized');
99
224
  ```
100
225
 
101
226
  **Log Output:**
@@ -103,7 +228,7 @@ logger.error({ error: err.message }, 'Failed to process');
103
228
  {
104
229
  "level": "info",
105
230
  "time": 1703318400000,
106
- "service": "neoiq-auth-service",
231
+ "service": "auth-service",
107
232
  "traceId": "abc123...",
108
233
  "spanId": "def456...",
109
234
  "correlationId": "req-789",
@@ -112,14 +237,12 @@ logger.error({ error: err.message }, 'Failed to process');
112
237
  }
113
238
  ```
114
239
 
115
- ### `getMeter(name, version?)`
240
+ ### `foundation.getMeter(name, version?)`
116
241
 
117
242
  Get a meter for custom metrics.
118
243
 
119
244
  ```typescript
120
- import { getMeter } from '@commerceiq/neoiq-foundation-node';
121
-
122
- const meter = getMeter('neoiq-auth-service');
245
+ const meter = foundation.getMeter('auth-service');
123
246
 
124
247
  // Counter
125
248
  const counter = meter.createCounter('logins.total');
@@ -128,19 +251,19 @@ counter.add(1, { provider: 'workos' });
128
251
  // Histogram
129
252
  const histogram = meter.createHistogram('token.validation.duration');
130
253
  histogram.record(150, { status: 'success' });
254
+
255
+ // Observable Gauge
256
+ meter.createObservableGauge('active_sessions', {}, (result) => {
257
+ result.observe(getActiveSessions());
258
+ });
131
259
  ```
132
260
 
133
- ### `observabilityPlugin`
261
+ ### `foundation.fastifyPlugin`
134
262
 
135
263
  Fastify plugin for automatic request handling.
136
264
 
137
265
  ```typescript
138
- import { observabilityPlugin } from '@commerceiq/neoiq-foundation-node';
139
-
140
- app.register(observabilityPlugin, {
141
- serviceName: 'my-service',
142
- excludeRoutes: ['/health', '/metrics'],
143
- });
266
+ app.register(foundation.fastifyPlugin);
144
267
  ```
145
268
 
146
269
  **Automatically:**
@@ -148,19 +271,27 @@ app.register(observabilityPlugin, {
148
271
  - Creates OpenTelemetry spans for each request
149
272
  - Logs request received/completed with full context
150
273
  - Records HTTP metrics (http.server.requests.total, http.server.request.duration)
274
+ - Skips health check endpoints (/health, /healthz, /ready, /live)
151
275
 
152
- ### `createHttpClient(options)`
276
+ ### `foundation.createHttpClient(options)`
153
277
 
154
278
  Create an Axios client with full observability.
155
279
 
156
280
  ```typescript
157
- import { createHttpClient } from '@commerceiq/neoiq-foundation-node';
158
-
159
- const userClient = createHttpClient({
281
+ const userClient = foundation.createHttpClient({
160
282
  baseURL: 'http://user-service:3000',
161
283
  serviceName: 'user-service',
162
284
  timeout: 10000,
163
- retry: { retries: 3 },
285
+ retry: {
286
+ retries: 3,
287
+ retryDelay: 1000,
288
+ retryStatusCodes: [408, 429, 500, 502, 503, 504],
289
+ },
290
+ circuitBreaker: {
291
+ enabled: true,
292
+ resetTimeout: 30000,
293
+ errorThresholdPercentage: 50,
294
+ },
164
295
  });
165
296
 
166
297
  const response = await userClient.get('/api/users/123');
@@ -174,16 +305,14 @@ const response = await userClient.get('/api/users/123');
174
305
  - Retries on 5xx errors with exponential backoff
175
306
  - Circuit breaker protection
176
307
 
177
- ### `shutdown()`
308
+ ### `foundation.shutdown()`
178
309
 
179
310
  Gracefully shutdown OTEL providers. Call on application shutdown.
180
311
 
181
312
  ```typescript
182
- import { shutdown } from '@commerceiq/neoiq-foundation-node';
183
-
184
313
  process.on('SIGTERM', async () => {
185
314
  await app.close();
186
- await shutdown(); // Flush telemetry
315
+ await foundation.shutdown(); // Flush telemetry
187
316
  });
188
317
  ```
189
318
 
@@ -192,47 +321,54 @@ process.on('SIGTERM', async () => {
192
321
  ## Complete Example
193
322
 
194
323
  ```typescript
195
- // src/index.ts
196
-
197
- // STEP 1: Initialize OTEL (must be first!)
198
- import { init, logger, getMeter, observabilityPlugin, createHttpClient, shutdown } from '@commerceiq/neoiq-foundation-node';
324
+ // src/foundation.ts
325
+ import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
199
326
 
200
- init({ serviceName: 'neoiq-auth-service' });
327
+ export const foundation = createFoundation({
328
+ serviceName: 'auth-service',
329
+ environment: process.env.NODE_ENV as 'development' | 'production',
330
+ });
331
+ ```
201
332
 
202
- // STEP 2: Import dependencies
333
+ ```typescript
334
+ // src/index.ts
203
335
  import Fastify from 'fastify';
336
+ import { foundation } from './foundation';
204
337
 
205
338
  const app = Fastify();
206
339
 
207
- // STEP 3: Register plugin
208
- app.register(observabilityPlugin, { serviceName: 'neoiq-auth-service' });
340
+ // Register observability plugin
341
+ app.register(foundation.fastifyPlugin);
209
342
 
210
- // STEP 4: Create HTTP clients
211
- const userClient = createHttpClient({
212
- baseURL: process.env.USER_SERVICE_URL,
343
+ // Create HTTP clients
344
+ const userClient = foundation.createHttpClient({
345
+ baseURL: process.env.USER_SERVICE_URL!,
213
346
  serviceName: 'user-service',
214
347
  });
215
348
 
216
- // STEP 5: Custom metrics
217
- const meter = getMeter('neoiq-auth-service');
349
+ // Custom metrics
350
+ const meter = foundation.getMeter('auth-service');
218
351
  const loginCounter = meter.createCounter('auth.logins.total');
219
352
 
220
- // STEP 6: Routes
353
+ // Routes
221
354
  app.post('/api/v1/auth/login', async (req, reply) => {
222
- const { provider } = req.body as any;
355
+ const { provider } = req.body as { provider: string };
223
356
 
224
357
  loginCounter.add(1, { provider });
225
- logger.info({ provider }, 'Login attempt');
358
+ foundation.logger.info({ provider }, 'Login attempt');
226
359
 
227
- return { success: true };
360
+ // Call user service (trace context automatically propagated)
361
+ const { data: user } = await userClient.get('/api/users/me');
362
+
363
+ return { success: true, user };
228
364
  });
229
365
 
230
- // STEP 7: Start with graceful shutdown
366
+ // Start with graceful shutdown
231
367
  app.listen({ port: 3000 });
232
368
 
233
369
  process.on('SIGTERM', async () => {
234
370
  await app.close();
235
- await shutdown(); // Flush telemetry
371
+ await foundation.shutdown();
236
372
  });
237
373
  ```
238
374
 
@@ -243,8 +379,8 @@ process.on('SIGTERM', async () => {
243
379
  | Variable | Description | Default |
244
380
  |----------|-------------|---------|
245
381
  | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTEL Collector URL | `http://otel-stack-deployment-collector.observability.svc.cluster.local:4317` |
246
- | `OTEL_SERVICE_VERSION` | Service version | `1.0.0` |
247
- | `OTEL_ENVIRONMENT` | Deployment environment | `development` |
382
+ | `SERVICE_VERSION` | Service version | `1.0.0` |
383
+ | `NODE_ENV` | Environment | `development` |
248
384
  | `LOG_LEVEL` | Log level | `info` |
249
385
 
250
386
  ---
@@ -272,6 +408,25 @@ process.on('SIGTERM', async () => {
272
408
 
273
409
  ---
274
410
 
411
+ ## Project Structure
412
+
413
+ ```
414
+ src/
415
+ ├── index.ts # Main exports
416
+ ├── config.ts # Zod configuration schemas
417
+ ├── foundation.ts # createFoundation() entry point
418
+ ├── features/
419
+ │ ├── context.ts # AsyncLocalStorage context manager
420
+ │ ├── logging.ts # Pino logger setup
421
+ │ ├── tracing.ts # OTEL trace provider
422
+ │ └── metrics.ts # OTEL meter provider
423
+ └── integrations/
424
+ ├── fastify-plugin.ts # Fastify observability plugin
425
+ └── http-client.ts # Axios wrapper with observability
426
+ ```
427
+
428
+ ---
429
+
275
430
  ## Development
276
431
 
277
432
  ```bash
@@ -283,6 +438,12 @@ npm run build
283
438
 
284
439
  # Type check
285
440
  npm run typecheck
441
+
442
+ # Lint
443
+ npm run lint
444
+
445
+ # Format
446
+ npm run prettier:write
286
447
  ```
287
448
 
288
449
  ---
@@ -290,4 +451,3 @@ npm run typecheck
290
451
  ## License
291
452
 
292
453
  MIT
293
-