@ciq-dev/neoiq-foundation-node 1.0.0-beta.1 → 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 +223 -63
- package/dist/index.d.mts +573 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.d.ts +570 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +885 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +832 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +26 -13
- package/dist/http-client.d.ts +0 -80
- package/dist/http-client.d.ts.map +0 -1
- package/dist/http-client.js +0 -188
- package/dist/http-client.js.map +0 -1
- package/dist/observability.d.ts +0 -132
- package/dist/observability.d.ts.map +0 -1
- package/dist/observability.js +0 -246
- package/dist/observability.js.map +0 -1
- package/dist/plugin.d.ts +0 -40
- package/dist/plugin.d.ts.map +0 -1
- package/dist/plugin.js +0 -176
- package/dist/plugin.js.map +0 -1
package/README.md
CHANGED
|
@@ -20,9 +20,13 @@ 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
|
|
@@ -31,7 +35,7 @@ Node.js observability foundation for CommerceIQ services. Integrates with **Grou
|
|
|
31
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
|
|
@@ -40,22 +44,26 @@ npm install @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-metric
|
|
|
40
44
|
### 2. Initialize (First Line of the App)
|
|
41
45
|
|
|
42
46
|
```typescript
|
|
43
|
-
import {
|
|
47
|
+
import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
|
|
44
48
|
|
|
45
|
-
//
|
|
46
|
-
|
|
47
|
-
serviceName: '
|
|
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(
|
|
66
|
+
app.register(foundation.fastifyPlugin);
|
|
59
67
|
```
|
|
60
68
|
|
|
61
69
|
### 4. Set Environment Variable in Kubernetes
|
|
@@ -70,32 +78,149 @@ That's it! Traces and metrics will flow to Groundcover.
|
|
|
70
78
|
|
|
71
79
|
---
|
|
72
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
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
73
122
|
## API Reference
|
|
74
123
|
|
|
75
|
-
### `
|
|
124
|
+
### `createFoundation(options)`
|
|
76
125
|
|
|
77
|
-
|
|
126
|
+
Create a foundation instance with full observability capabilities.
|
|
78
127
|
|
|
79
128
|
```typescript
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
###
|
|
173
|
+
### Foundation Instance
|
|
91
174
|
|
|
92
|
-
|
|
175
|
+
The foundation instance provides access to all observability features:
|
|
93
176
|
|
|
94
177
|
```typescript
|
|
95
|
-
|
|
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
|
-
|
|
98
|
-
|
|
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": "
|
|
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
|
-
|
|
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
|
-
### `
|
|
261
|
+
### `foundation.fastifyPlugin`
|
|
134
262
|
|
|
135
263
|
Fastify plugin for automatic request handling.
|
|
136
264
|
|
|
137
265
|
```typescript
|
|
138
|
-
|
|
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
|
-
|
|
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: {
|
|
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 '@ciq-dev/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/
|
|
196
|
-
|
|
197
|
-
// STEP 1: Initialize OTEL (must be first!)
|
|
198
|
-
import { init, logger, getMeter, observabilityPlugin, createHttpClient, shutdown } from '@ciq-dev/neoiq-foundation-node';
|
|
324
|
+
// src/foundation.ts
|
|
325
|
+
import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
|
|
199
326
|
|
|
200
|
-
|
|
327
|
+
export const foundation = createFoundation({
|
|
328
|
+
serviceName: 'auth-service',
|
|
329
|
+
environment: process.env.NODE_ENV as 'development' | 'production',
|
|
330
|
+
});
|
|
331
|
+
```
|
|
201
332
|
|
|
202
|
-
|
|
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
|
-
//
|
|
208
|
-
app.register(
|
|
340
|
+
// Register observability plugin
|
|
341
|
+
app.register(foundation.fastifyPlugin);
|
|
209
342
|
|
|
210
|
-
//
|
|
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
|
-
//
|
|
217
|
-
const meter = getMeter('
|
|
349
|
+
// Custom metrics
|
|
350
|
+
const meter = foundation.getMeter('auth-service');
|
|
218
351
|
const loginCounter = meter.createCounter('auth.logins.total');
|
|
219
352
|
|
|
220
|
-
//
|
|
353
|
+
// Routes
|
|
221
354
|
app.post('/api/v1/auth/login', async (req, reply) => {
|
|
222
|
-
const { provider } = req.body as
|
|
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
|
-
|
|
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
|
-
//
|
|
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();
|
|
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
|
-
| `
|
|
247
|
-
| `
|
|
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
|
-
|