@ciq-dev/neoiq-foundation-node 1.1.2-beta.3 → 1.1.2-beta.5

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,295 +1,136 @@
1
1
  # @ciq-dev/neoiq-foundation-node
2
2
 
3
- Node.js observability foundation for CommerceIQ services. Integrates with **Groundcover** via **OpenTelemetry**.
3
+ Node.js observability foundation for CommerceIQ services OpenTelemetry + Groundcover.
4
4
 
5
5
  ```
6
- ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
7
- │ Our Service │────▶│ OTEL Collector │────▶│ Groundcover │
8
- (auth-service) │ │ (in-cluster) │ │ (dashboard) │
9
- └─────────────────┘ └─────────────────┘ └─────────────────┘
10
- │ │
11
- ├── Traces (OTLP) ───────┤
12
- └── Metrics (OTLP) ──────┘
13
-
14
-
15
- └── Logs (stdout) ──────▶ Groundcover Agent scrapes container logs
6
+ Service → OTEL Collector → Groundcover
7
+ ├── Traces (OTLP gRPC)
8
+ ├── Metrics (OTLP gRPC)
9
+ └── Logs (stdout → scraped by Groundcover Agent)
16
10
  ```
17
11
 
18
12
  ## Features
19
13
 
20
- - **Traces**: Automatic span creation for HTTP requests (incoming & outgoing)
21
- - **Metrics**: HTTP request counts, durations, errors + custom business metrics
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
25
- - **Fastify Plugin**: One-line integration for request lifecycle
26
- - **HTTP Client**: Axios wrapper with retry, circuit breaker, and trace propagation
27
- - **Object Store Adapter**: Provider-agnostic wrapper for cloud object storage (e.g. S3) to avoid direct SDK coupling
14
+ - Distributed tracing, metrics, structured logs via OpenTelemetry + Pino
15
+ - Fastify plugin for automatic request lifecycle instrumentation
16
+ - Axios HTTP client with retry, circuit breaker, and trace propagation
17
+ - AsyncLocalStorage context manager (correlationId across async calls)
18
+ - HashiCorp Vault secrets client
19
+ - Provider-agnostic object store (S3 + in-memory)
20
+ - Zod-validated config with helpful error messages
28
21
 
29
22
  ---
30
23
 
31
- ## Quick Start
32
-
33
- ### 1. Install
24
+ ## Install
34
25
 
35
26
  ```bash
36
27
  npm install @ciq-dev/neoiq-foundation-node
37
28
 
38
- # Peer dependencies
39
- npm install zod @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-metrics \
29
+ # Peer deps
30
+ npm install zod pino pino-pretty axios axios-retry opossum fastify fastify-plugin \
31
+ @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/sdk-metrics \
40
32
  @opentelemetry/exporter-trace-otlp-grpc @opentelemetry/exporter-metrics-otlp-grpc \
41
- @opentelemetry/auto-instrumentations-node @opentelemetry/resources @opentelemetry/semantic-conventions \
42
- pino pino-pretty axios axios-retry opossum fastify-plugin
33
+ @opentelemetry/auto-instrumentations-node @opentelemetry/resources \
34
+ @opentelemetry/semantic-conventions
43
35
 
44
- # Optional (only if you want AWS S3 implementation of ObjectStore)
36
+ # Optional AWS S3 object store
45
37
  npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
46
38
  ```
47
39
 
48
- ### 2. Initialize (First Line of the App)
40
+ ---
41
+
42
+ ## Quick Start
49
43
 
50
44
  ```typescript
45
+ // src/foundation.ts — import this FIRST in the app
51
46
  import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
52
47
 
53
- // Create foundation instance (must be called BEFORE other imports)
54
- const foundation = createFoundation({
55
- serviceName: 'auth-service',
56
- serviceVersion: '1.0.0',
48
+ export const foundation = createFoundation({
49
+ serviceName: 'my-service',
50
+ serviceVersion: '1.0.0', // default: $SERVICE_VERSION or '1.0.0'
51
+ environment: 'production', // 'development' | 'staging' | 'qa' | 'production'
57
52
  });
58
-
59
- // Export for use in other files
60
- export { foundation };
61
53
  ```
62
54
 
63
- ### 3. Add Fastify Plugin
64
-
65
55
  ```typescript
56
+ // src/index.ts
66
57
  import Fastify from 'fastify';
67
58
  import { foundation } from './foundation';
68
59
 
69
60
  const app = Fastify();
70
- app.register(foundation.fastifyPlugin);
71
- ```
72
-
73
- ### 4. Set Environment Variable in Kubernetes
74
-
75
- ```yaml
76
- env:
77
- - name: OTEL_EXPORTER_OTLP_ENDPOINT
78
- value: 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317'
79
- ```
80
-
81
- That's it! Traces and metrics will flow to Groundcover.
82
-
83
- ---
84
-
85
- ## Selective Feature Enablement
61
+ app.register(foundation.fastifyPlugin); // automatic request tracing/metrics/logging
86
62
 
87
- Enable only the features you need for lightweight deployments:
88
-
89
- ```typescript
90
- import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
91
-
92
- // Full observability (default)
93
- const foundation = createFoundation({
94
- serviceName: 'auth-service',
95
- });
96
-
97
- // Logging only (lightweight mode)
98
- const foundation = createFoundation({
99
- serviceName: 'simple-worker',
100
- features: {
101
- tracing: false,
102
- metrics: false,
103
- logging: true,
104
- },
63
+ process.on('SIGTERM', async () => {
64
+ await app.close();
65
+ await foundation.lifecycle.shutdown();
105
66
  });
67
+ ```
106
68
 
107
- // Custom auto-instrumentation
108
- const foundation = createFoundation({
109
- serviceName: 'api-gateway',
110
- features: {
111
- tracing: true,
112
- metrics: true,
113
- autoInstrumentation: {
114
- http: true,
115
- fastify: true,
116
- mongodb: false, // Not using MongoDB
117
- redis: false, // Not using Redis
118
- pg: true, // Using PostgreSQL
119
- },
120
- },
121
- });
69
+ ```yaml
70
+ # Kubernetes env
71
+ - name: OTEL_EXPORTER_OTLP_ENDPOINT
72
+ value: 'http://otel-stack-deployment-collector.observability.svc.cluster.local:4317'
122
73
  ```
123
74
 
124
75
  ---
125
76
 
126
- ## API Reference
77
+ ## API
127
78
 
128
- ### `createFoundation(options)`
79
+ The foundation instance exposes top-level shortcuts for the most common operations and sub-modules for everything else.
129
80
 
130
- Create a foundation instance with full observability capabilities.
81
+ ### Top-level shortcuts
131
82
 
132
83
  ```typescript
133
- import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
134
-
135
- const foundation = createFoundation({
136
- // Required
137
- serviceName: 'my-service',
138
-
139
- // Optional
140
- serviceVersion: '1.0.0', // Default: '1.0.0' or $SERVICE_VERSION
141
- environment: 'production', // Default: 'development' or $NODE_ENV
142
-
143
- // Feature toggles
144
- features: {
145
- tracing: true, // Enable distributed tracing
146
- metrics: true, // Enable metrics collection
147
- logging: true, // Enable structured logging
148
- autoInstrumentation: { // Fine-grained control
149
- http: true,
150
- fastify: true,
151
- express: true,
152
- mongodb: true,
153
- pg: true,
154
- mysql: true,
155
- redis: true,
156
- ioredis: true,
157
- fs: false, // Disabled by default (noisy)
158
- dns: false, // Disabled by default
159
- },
160
- },
161
-
162
- // OTEL configuration
163
- otel: {
164
- endpoint: 'http://...:4317', // Default: cluster OTEL Collector
165
- metricsIntervalMs: 5000, // Default: 5000 (5 seconds)
166
- traceSampleRate: 1.0, // Default: 1.0 (100%)
167
- },
168
-
169
- // Logging configuration
170
- logging: {
171
- level: 'info', // 'debug' | 'info' | 'warn' | 'error'
172
- prettyPrint: false, // Auto-detect from environment
173
- },
174
- });
84
+ foundation.logger // Pino logger with trace context auto-injected
85
+ foundation.context // AsyncLocalStorage context manager
86
+ foundation.fastifyPlugin // Fastify plugin (register directly)
87
+ foundation.features // { tracing: boolean, metrics: boolean, logging: boolean }
88
+ foundation.config // Full resolved config
175
89
  ```
176
90
 
177
- ### Foundation Instance
178
-
179
- The foundation instance provides access to all observability features:
91
+ ### `foundation.observability`
180
92
 
181
93
  ```typescript
182
- // Logging
94
+ // Structured logging — logs include traceId, spanId, correlationId automatically
183
95
  foundation.logger.info({ userId: '123' }, 'User logged in');
184
- foundation.logger.error({ error: err.message }, 'Failed to process');
96
+ const routeLogger = foundation.logger.child({ component: 'auth' });
185
97
 
186
- // Metrics
187
- const meter = foundation.getMeter('my-service');
188
- const counter = meter.createCounter('logins.total');
189
- counter.add(1, { provider: 'workos' });
98
+ // Custom metrics
99
+ const meter = foundation.observability.getMeter('my-service');
100
+ const loginCounter = meter.createCounter('auth.logins.total');
101
+ loginCounter.add(1, { provider: 'workos' });
190
102
 
191
- // Tracing
192
- const tracer = foundation.getTracer();
103
+ const latency = meter.createHistogram('auth.token.duration');
104
+ latency.record(42, { status: 'success' });
105
+
106
+ // Custom spans
107
+ const tracer = foundation.observability.getTracer();
193
108
  tracer.startActiveSpan('validateToken', (span) => {
194
- // ... do work
109
+ // ... work
195
110
  span.end();
196
111
  });
197
112
 
198
- // HTTP Client
199
- const client = foundation.createHttpClient({
200
- baseURL: 'http://user-service:3000',
201
- serviceName: 'user-service',
113
+ // Convenience: span with auto error handling
114
+ await foundation.observability.trace('validateToken', async () => {
115
+ // ... work
202
116
  });
203
117
 
204
- // Fastify Plugin
205
- app.register(foundation.fastifyPlugin);
206
-
207
- // Shutdown
208
- await foundation.shutdown();
209
-
210
- // Check features
211
- console.log(foundation.features);
212
- // { tracing: true, metrics: true, logging: true }
118
+ // Get current trace context
119
+ const { traceId, spanId } = foundation.observability.getTraceContext();
213
120
  ```
214
121
 
215
- ### `foundation.logger`
216
-
217
- Structured logger with automatic trace context injection.
122
+ ### `foundation.http`
218
123
 
219
124
  ```typescript
220
- foundation.logger.info({ userId: '123' }, 'User logged in');
221
- foundation.logger.error({ error: err.message }, 'Failed to process');
222
- foundation.logger.warn({ attempt: 3 }, 'Retry attempt');
223
- foundation.logger.debug({ payload: data }, 'Processing request');
224
-
225
- // Child logger for component-specific logging
226
- const authLogger = foundation.logger.child({ component: 'auth' });
227
- authLogger.info({}, 'Auth module initialized');
228
- ```
229
-
230
- **Log Output:**
231
- ```json
232
- {
233
- "level": "info",
234
- "time": 1703318400000,
235
- "service": "auth-service",
236
- "traceId": "abc123...",
237
- "spanId": "def456...",
238
- "correlationId": "req-789",
239
- "userId": "123",
240
- "msg": "User logged in"
241
- }
242
- ```
243
-
244
- ### `foundation.getMeter(name, version?)`
245
-
246
- Get a meter for custom metrics.
247
-
248
- ```typescript
249
- const meter = foundation.getMeter('auth-service');
250
-
251
- // Counter
252
- const counter = meter.createCounter('logins.total');
253
- counter.add(1, { provider: 'workos' });
254
-
255
- // Histogram
256
- const histogram = meter.createHistogram('token.validation.duration');
257
- histogram.record(150, { status: 'success' });
258
-
259
- // Observable Gauge
260
- meter.createObservableGauge('active_sessions', {}, (result) => {
261
- result.observe(getActiveSessions());
262
- });
263
- ```
264
-
265
- ### `foundation.fastifyPlugin`
266
-
267
- Fastify plugin for automatic request handling.
268
-
269
- ```typescript
270
- app.register(foundation.fastifyPlugin);
271
- ```
272
-
273
- **Automatically:**
274
- - Extracts/generates correlation ID (x-request-id header)
275
- - Creates OpenTelemetry spans for each request
276
- - Logs request received/completed with full context
277
- - Records HTTP metrics (http.server.requests.total, http.server.request.duration)
278
- - Skips health check endpoints (/health, /healthz, /ready, /live)
279
-
280
- ### `foundation.createHttpClient(options)`
281
-
282
- Create an Axios client with full observability.
283
-
284
- ```typescript
285
- const userClient = foundation.createHttpClient({
125
+ const userClient = foundation.http.createClient({
286
126
  baseURL: 'http://user-service:3000',
287
127
  serviceName: 'user-service',
288
128
  timeout: 10000,
289
129
  retry: {
290
130
  retries: 3,
291
131
  retryDelay: 1000,
292
- retryStatusCodes: [408, 429, 500, 502, 503, 504],
132
+ retryStatusCodes: [429, 500, 502, 503, 504],
133
+ retryNonIdempotent: false, // default: don't retry POST/PATCH
293
134
  },
294
135
  circuitBreaker: {
295
136
  enabled: true,
@@ -298,192 +139,200 @@ const userClient = foundation.createHttpClient({
298
139
  },
299
140
  });
300
141
 
301
- const response = await userClient.get('/api/users/123');
142
+ const { data } = await userClient.get('/api/users/123');
143
+ // Trace context (traceparent) and correlationId propagated automatically
302
144
  ```
303
145
 
304
- **Automatically:**
305
- - Propagates trace context (traceparent header)
306
- - Propagates correlation ID (x-request-id header)
307
- - Logs outbound requests/responses
308
- - Records HTTP client metrics
309
- - Retries on 5xx errors with exponential backoff
310
- - Circuit breaker protection
311
-
312
- ### `foundation.shutdown()`
313
-
314
- Gracefully shutdown OTEL providers. Call on application shutdown.
146
+ ### `foundation.lifecycle`
315
147
 
316
148
  ```typescript
317
- process.on('SIGTERM', async () => {
318
- await app.close();
319
- await foundation.shutdown(); // Flush telemetry
320
- });
321
- ```
149
+ // Graceful shutdown flushes OTEL providers
150
+ await foundation.lifecycle.shutdown();
322
151
 
323
- ---
152
+ // Readiness check — false if providers are still initializing
153
+ foundation.lifecycle.isReady();
324
154
 
325
- ## Object Store Adapter (S3 / Cloud Abstraction)
155
+ // Structured health status
156
+ const health = foundation.lifecycle.health();
157
+ // { status: 'healthy'|'degraded'|'unhealthy', components: { tracing, metrics, logging } }
326
158
 
327
- Use this when you want to **decouple application code** from `aws-sdk` / cloud vendor SDKs.
159
+ // Run with error capture (useful for background tasks)
160
+ const result = await foundation.lifecycle.safeRun(
161
+ () => riskyOperation(),
162
+ defaultValue, // returned on error
163
+ );
164
+ ```
328
165
 
329
- ### AWS S3 (optional peer deps)
166
+ ### `foundation.secrets` (Vault)
330
167
 
331
168
  ```typescript
332
- import { AwsS3ObjectStore } from '@ciq-dev/neoiq-foundation-node';
333
-
334
- const store = new AwsS3ObjectStore({
335
- clientOptions: { region: process.env.AWS_REGION || 'us-east-1' },
169
+ // Enable in config:
170
+ const foundation = createFoundation({
171
+ serviceName: 'my-service',
172
+ vault: {
173
+ enabled: true,
174
+ address: 'https://vault.beta.commerceiq.ai',
175
+ authMethod: 'kubernetes', // or 'token'
176
+ role: 'my-service',
177
+ },
336
178
  });
337
179
 
338
- const ref = { bucket: 'my-bucket', key: 'path/to/file.json' };
339
-
340
- // Upload
341
- await store.putObject(ref, JSON.stringify({ ok: true }), { contentType: 'application/json' });
342
-
343
- // Presigned PUT URL (browser upload)
344
- const uploadUrl = await store.presignPutObject(ref, { expiresInSeconds: 60, contentType: 'application/json' });
180
+ // Use:
181
+ const secrets = foundation.secrets!;
182
+ const creds = await secrets.getSecret('path/to/secret');
183
+ const apiKey = await secrets.getSecretValue('path/to/secret', 'API_KEY');
345
184
  ```
346
185
 
347
- ### In-memory (local/tests)
186
+ ---
187
+
188
+ ## Full Config Reference
348
189
 
349
190
  ```typescript
350
- import { InMemoryObjectStore } from '@ciq-dev/neoiq-foundation-node';
191
+ createFoundation({
192
+ serviceName: 'my-service', // required
193
+ serviceVersion: '1.0.0', // default: $SERVICE_VERSION
194
+ environment: 'development', // 'development'|'staging'|'qa'|'production'
351
195
 
352
- const store = new InMemoryObjectStore();
353
- await store.putObject({ bucket: 'b', key: 'k' }, 'hello');
354
- const obj = await store.getObject({ bucket: 'b', key: 'k' });
355
- ```
196
+ features: {
197
+ tracing: true,
198
+ metrics: true,
199
+ logging: true,
200
+ autoInstrumentation: {
201
+ http: true, fastify: true, express: true,
202
+ mongodb: true, pg: true, mysql: true,
203
+ redis: true, ioredis: true, grpc: true,
204
+ fs: false, // disabled by default (noisy)
205
+ dns: false,
206
+ },
207
+ },
356
208
 
357
- ---
209
+ otel: {
210
+ endpoint: 'http://...:4317', // default: $OTEL_EXPORTER_OTLP_ENDPOINT or cluster URL
211
+ metricsIntervalMs: 5000,
212
+ },
358
213
 
359
- ## Complete Example
214
+ logging: {
215
+ level: 'info', // 'debug'|'info'|'warn'|'error'
216
+ prettyPrint: false, // default: true in development
217
+ },
360
218
 
361
- ```typescript
362
- // src/foundation.ts
363
- import { createFoundation } from '@ciq-dev/neoiq-foundation-node';
219
+ requestLogging: {
220
+ logHeaders: true,
221
+ logBody: false,
222
+ logResponseBody: false,
223
+ maxBodySize: 10240,
224
+ redactHeaders: ['authorization', 'cookie'],
225
+ },
364
226
 
365
- export const foundation = createFoundation({
366
- serviceName: 'auth-service',
367
- environment: process.env.NODE_ENV as 'development' | 'production',
227
+ redaction: {
228
+ additionalPaths: ['req.body.password', 'res.body.token'],
229
+ },
230
+
231
+ shutdown: {
232
+ flushOnCrash: false, // flush telemetry on uncaughtException/unhandledRejection
233
+ flushTimeoutMs: 5000,
234
+ },
235
+
236
+ vault: {
237
+ enabled: false,
238
+ address: 'https://vault.beta.commerceiq.ai',
239
+ authMethod: 'kubernetes',
240
+ role: 'my-service',
241
+ mountPoint: 'secret',
242
+ tokenPath: '/var/run/secrets/kubernetes.io/serviceaccount/token',
243
+ k8sAuthMount: 'kubernetes',
244
+ },
368
245
  });
369
246
  ```
370
247
 
371
- ```typescript
372
- // src/index.ts
373
- import Fastify from 'fastify';
374
- import { foundation } from './foundation';
248
+ ---
375
249
 
376
- const app = Fastify();
250
+ ## Object Store
377
251
 
378
- // Register observability plugin
379
- app.register(foundation.fastifyPlugin);
252
+ Provider-agnostic abstraction over cloud object storage.
380
253
 
381
- // Create HTTP clients
382
- const userClient = foundation.createHttpClient({
383
- baseURL: process.env.USER_SERVICE_URL!,
384
- serviceName: 'user-service',
385
- });
254
+ ```typescript
255
+ import { AwsS3ObjectStore, InMemoryObjectStore } from '@ciq-dev/neoiq-foundation-node';
386
256
 
387
- // Custom metrics
388
- const meter = foundation.getMeter('auth-service');
389
- const loginCounter = meter.createCounter('auth.logins.total');
257
+ // Production
258
+ const store = new AwsS3ObjectStore({ clientOptions: { region: 'us-east-1' } });
390
259
 
391
- // Routes
392
- app.post('/api/v1/auth/login', async (req, reply) => {
393
- const { provider } = req.body as { provider: string };
394
-
395
- loginCounter.add(1, { provider });
396
- foundation.logger.info({ provider }, 'Login attempt');
397
-
398
- // Call user service (trace context automatically propagated)
399
- const { data: user } = await userClient.get('/api/users/me');
400
-
401
- return { success: true, user };
402
- });
260
+ // Tests
261
+ const store = new InMemoryObjectStore();
403
262
 
404
- // Start with graceful shutdown
405
- app.listen({ port: 3000 });
263
+ const ref = { bucket: 'my-bucket', key: 'path/to/file.json' };
264
+ await store.putObject(ref, JSON.stringify(data), { contentType: 'application/json' });
265
+ const obj = await store.getObject(ref);
406
266
 
407
- process.on('SIGTERM', async () => {
408
- await app.close();
409
- await foundation.shutdown();
410
- });
267
+ // Presigned upload URL
268
+ const url = await store.presignPutObject(ref, { expiresInSeconds: 60, contentType: 'application/json' });
411
269
  ```
412
270
 
413
271
  ---
414
272
 
415
- ## Environment Variables
273
+ ## Fastify Plugin — What it does
416
274
 
417
- | Variable | Description | Default |
418
- |----------|-------------|---------|
419
- | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTEL Collector URL | `http://otel-stack-deployment-collector.observability.svc.cluster.local:4317` |
420
- | `SERVICE_VERSION` | Service version | `1.0.0` |
421
- | `NODE_ENV` | Environment | `development` |
422
- | `LOG_LEVEL` | Log level | `info` |
275
+ `foundation.fastifyPlugin` automatically:
276
+ - Extracts/generates `correlationId` from `x-request-id`
277
+ - Creates an OTEL span per request
278
+ - Logs `request received` / `request completed` with full context
279
+ - Records `http.server.requests.total` and `http.server.request.duration` metrics
280
+ - Skips `/health`, `/healthz`, `/ready`, `/live`
423
281
 
424
282
  ---
425
283
 
426
- ## What Gets Sent to Groundcover
427
-
428
- ### Traces
429
- - Every HTTP request (incoming and outgoing)
430
- - Correlation ID linking requests across services
431
- - Span attributes: method, url, status_code, duration
284
+ ## Metrics Reference
432
285
 
433
- ### Metrics
434
- - `http.server.requests.total` - Incoming request count
435
- - `http.server.request.duration` - Incoming request latency
436
- - `http.server.requests.errors` - Incoming request errors
437
- - `http.client.requests.total` - Outgoing request count
438
- - `http.client.request.duration` - Outgoing request latency
439
- - Custom business metrics you define
440
-
441
- ### Logs (via stdout)
442
- - Structured JSON logs written to stdout (Pino)
443
- - Include traceId, spanId, correlationId for correlation
444
- - Groundcover Agent scrapes container logs
445
- - Automatically correlated with traces in Groundcover dashboard
286
+ | Metric | Description |
287
+ |--------|-------------|
288
+ | `http.server.requests.total` | Incoming request count |
289
+ | `http.server.request.duration` | Incoming request latency (histogram) |
290
+ | `http.server.requests.errors` | Incoming request errors |
291
+ | `http.client.requests.total` | Outgoing request count |
292
+ | `http.client.request.duration` | Outgoing request latency (histogram) |
446
293
 
447
294
  ---
448
295
 
449
- ## Project Structure
296
+ ## Environment Variables
450
297
 
451
- ```
452
- src/
453
- ├── index.ts # Main exports
454
- ├── config.ts # Zod configuration schemas
455
- ├── foundation.ts # createFoundation() entry point
456
- ├── features/
457
- │ ├── context.ts # AsyncLocalStorage context manager
458
- │ ├── logging.ts # Pino logger setup
459
- │ ├── tracing.ts # OTEL trace provider
460
- │ └── metrics.ts # OTEL meter provider
461
- └── integrations/
462
- ├── fastify-plugin.ts # Fastify observability plugin
463
- ├── http-client.ts # Axios wrapper with observability
464
- └── object-store/ # Cloud object store abstraction (S3, etc.)
465
- ```
298
+ | Variable | Default |
299
+ |----------|---------|
300
+ | `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://otel-stack-deployment-collector.observability.svc.cluster.local:4317` |
301
+ | `SERVICE_VERSION` | `1.0.0` |
302
+ | `NODE_ENV` | `development` |
303
+ | `LOG_LEVEL` | `info` |
466
304
 
467
305
  ---
468
306
 
469
307
  ## Development
470
308
 
471
309
  ```bash
472
- # Install dependencies
473
- npm install
474
-
475
- # Build
476
- npm run build
477
-
478
- # Type check
479
- npm run typecheck
310
+ npm run build # tsdown: CJS + ESM dual output → dist/
311
+ npm run typecheck # tsc --noEmit
312
+ npm run lint # ESLint
313
+ npm run prettier:write # Prettier format
314
+ npm test # Vitest
315
+ npm run test:watch # Vitest watch mode
316
+ ```
480
317
 
481
- # Lint
482
- npm run lint
318
+ ---
483
319
 
484
- # Format
485
- npm run prettier:write
486
- ```
320
+ ## Deprecated API
321
+
322
+ The following flat methods on `foundation` still work but emit a deprecation warning. Migrate to sub-modules:
323
+
324
+ | Deprecated | Use instead |
325
+ |------------|-------------|
326
+ | `foundation.getMeter()` | `foundation.observability.getMeter()` |
327
+ | `foundation.getTracer()` | `foundation.observability.getTracer()` |
328
+ | `foundation.getTraceContext()` | `foundation.observability.getTraceContext()` |
329
+ | `foundation.getActiveSpan()` | `foundation.observability.getActiveSpan()` |
330
+ | `foundation.trace()` | `foundation.observability.trace()` |
331
+ | `foundation.createHttpClient()` | `foundation.http.createClient()` |
332
+ | `foundation.shutdown()` | `foundation.lifecycle.shutdown()` |
333
+ | `foundation.isReady()` | `foundation.lifecycle.isReady()` |
334
+ | `foundation.health()` | `foundation.lifecycle.health()` |
335
+ | `foundation.safeRun()` | `foundation.lifecycle.safeRun()` |
487
336
 
488
337
  ---
489
338