@digilogiclabs/platform-core 1.0.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 +545 -0
- package/dist/ConsoleEmail-XeUBAnwc.d.mts +2786 -0
- package/dist/ConsoleEmail-XeUBAnwc.d.ts +2786 -0
- package/dist/index-C_2W7Byw.d.mts +379 -0
- package/dist/index-C_2W7Byw.d.ts +379 -0
- package/dist/index.d.mts +2045 -0
- package/dist/index.d.ts +2045 -0
- package/dist/index.js +6690 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6550 -0
- package/dist/index.mjs.map +1 -0
- package/dist/migrate.js +1101 -0
- package/dist/migrate.js.map +1 -0
- package/dist/migrations/index.d.mts +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.js +508 -0
- package/dist/migrations/index.js.map +1 -0
- package/dist/migrations/index.mjs +468 -0
- package/dist/migrations/index.mjs.map +1 -0
- package/dist/testing.d.mts +97 -0
- package/dist/testing.d.ts +97 -0
- package/dist/testing.js +1789 -0
- package/dist/testing.js.map +1 -0
- package/dist/testing.mjs +1743 -0
- package/dist/testing.mjs.map +1 -0
- package/package.json +152 -0
package/README.md
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
# @digilogiclabs/platform-core
|
|
2
|
+
|
|
3
|
+
> Vendor-agnostic infrastructure abstraction layer for building portable, enterprise-grade applications.
|
|
4
|
+
|
|
5
|
+
[](./tests)
|
|
6
|
+
[](./src/adapters)
|
|
7
|
+
[](./src/interfaces)
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Platform Core provides a unified API for common infrastructure services, allowing you to swap providers without changing application code. Build once, deploy anywhere.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
### Core Infrastructure
|
|
16
|
+
- **Database** - Query builder abstraction (PostgreSQL, Supabase, Memory)
|
|
17
|
+
- **Cache** - Key-value caching (Redis, Upstash, Memory)
|
|
18
|
+
- **Storage** - File storage (S3/MinIO/R2, Supabase Storage, Memory)
|
|
19
|
+
- **Email** - Transactional email (SMTP, Resend, Console, Memory)
|
|
20
|
+
- **Queue** - Background jobs (BullMQ, Memory)
|
|
21
|
+
- **Tracing** - Distributed tracing (OpenTelemetry, Memory, Noop)
|
|
22
|
+
- **Health Checks** - Built-in health monitoring for all services
|
|
23
|
+
|
|
24
|
+
### Enterprise Patterns
|
|
25
|
+
- **Middleware** - Composable before/after hooks for all operations
|
|
26
|
+
- **Hooks** - Lifecycle events for database, cache, email, queue operations
|
|
27
|
+
- **Resilience** - Retry, circuit breaker, timeout, bulkhead, fallback patterns
|
|
28
|
+
- **Metrics** - Counters, gauges, histograms, timings with tag support
|
|
29
|
+
|
|
30
|
+
## Installation
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install @digilogiclabs/platform-core
|
|
34
|
+
# or
|
|
35
|
+
pnpm add @digilogiclabs/platform-core
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Optional Peer Dependencies
|
|
39
|
+
|
|
40
|
+
Install only the providers you need:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# For Supabase database
|
|
44
|
+
pnpm add @supabase/supabase-js
|
|
45
|
+
|
|
46
|
+
# For Upstash cache
|
|
47
|
+
pnpm add @upstash/redis
|
|
48
|
+
|
|
49
|
+
# For S3 storage
|
|
50
|
+
pnpm add @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
|
|
51
|
+
|
|
52
|
+
# For Resend email
|
|
53
|
+
pnpm add resend
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
### Development (Memory Adapters)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { createPlatform } from '@digilogiclabs/platform-core';
|
|
62
|
+
|
|
63
|
+
// Uses memory adapters by default - no external services needed
|
|
64
|
+
const platform = createPlatform();
|
|
65
|
+
|
|
66
|
+
// Use the platform
|
|
67
|
+
const users = await platform.db.from('users').where('active', '=', true).execute();
|
|
68
|
+
await platform.cache.set('user:123', users.data[0], 3600);
|
|
69
|
+
await platform.email.send({
|
|
70
|
+
to: 'user@example.com',
|
|
71
|
+
subject: 'Welcome!',
|
|
72
|
+
text: 'Hello from Platform Core',
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Production (Real Adapters)
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { createPlatformAsync } from '@digilogiclabs/platform-core';
|
|
80
|
+
|
|
81
|
+
// Use environment variables to configure providers
|
|
82
|
+
const platform = await createPlatformAsync();
|
|
83
|
+
|
|
84
|
+
// Or configure explicitly
|
|
85
|
+
const platform = await createPlatformAsync({
|
|
86
|
+
database: { provider: 'supabase' },
|
|
87
|
+
cache: { provider: 'upstash' },
|
|
88
|
+
storage: { provider: 's3' },
|
|
89
|
+
email: { provider: 'resend' },
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Environment Variables
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Provider selection
|
|
97
|
+
PLATFORM_DB_PROVIDER=memory|supabase
|
|
98
|
+
PLATFORM_CACHE_PROVIDER=memory|upstash
|
|
99
|
+
PLATFORM_STORAGE_PROVIDER=memory|s3|minio|r2
|
|
100
|
+
PLATFORM_EMAIL_PROVIDER=memory|console|resend
|
|
101
|
+
PLATFORM_QUEUE_PROVIDER=memory
|
|
102
|
+
|
|
103
|
+
# Supabase
|
|
104
|
+
SUPABASE_URL=https://your-project.supabase.co
|
|
105
|
+
SUPABASE_ANON_KEY=your-anon-key
|
|
106
|
+
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
|
|
107
|
+
|
|
108
|
+
# Upstash
|
|
109
|
+
UPSTASH_REDIS_REST_URL=https://your-redis.upstash.io
|
|
110
|
+
UPSTASH_REDIS_REST_TOKEN=your-token
|
|
111
|
+
|
|
112
|
+
# S3 / MinIO / R2
|
|
113
|
+
S3_ENDPOINT=https://s3.amazonaws.com
|
|
114
|
+
S3_REGION=us-east-1
|
|
115
|
+
S3_ACCESS_KEY_ID=your-key
|
|
116
|
+
S3_SECRET_ACCESS_KEY=your-secret
|
|
117
|
+
S3_BUCKET=your-bucket
|
|
118
|
+
S3_FORCE_PATH_STYLE=false
|
|
119
|
+
|
|
120
|
+
# Resend
|
|
121
|
+
RESEND_API_KEY=re_your_api_key
|
|
122
|
+
EMAIL_FROM=noreply@yourdomain.com
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## API Reference
|
|
126
|
+
|
|
127
|
+
### Platform
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
interface IPlatform {
|
|
131
|
+
db: IDatabase;
|
|
132
|
+
cache: ICache;
|
|
133
|
+
storage: IStorage;
|
|
134
|
+
email: IEmail;
|
|
135
|
+
queue: IQueue;
|
|
136
|
+
|
|
137
|
+
healthCheck(): Promise<PlatformHealthStatus>;
|
|
138
|
+
close(): Promise<void>;
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Database
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Query builder
|
|
146
|
+
const users = await platform.db
|
|
147
|
+
.from<User>('users')
|
|
148
|
+
.where('active', '=', true)
|
|
149
|
+
.orderBy('created_at', 'desc')
|
|
150
|
+
.limit(10)
|
|
151
|
+
.execute();
|
|
152
|
+
|
|
153
|
+
// Insert
|
|
154
|
+
await platform.db
|
|
155
|
+
.from<User>('users')
|
|
156
|
+
.insert({ name: 'John', email: 'john@example.com' })
|
|
157
|
+
.execute();
|
|
158
|
+
|
|
159
|
+
// Update
|
|
160
|
+
await platform.db
|
|
161
|
+
.from<User>('users')
|
|
162
|
+
.where('id', '=', '123')
|
|
163
|
+
.update({ name: 'Jane' })
|
|
164
|
+
.execute();
|
|
165
|
+
|
|
166
|
+
// Delete
|
|
167
|
+
await platform.db
|
|
168
|
+
.from<User>('users')
|
|
169
|
+
.where('id', '=', '123')
|
|
170
|
+
.delete()
|
|
171
|
+
.execute();
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### Cache
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Basic operations
|
|
178
|
+
await platform.cache.set('key', value, 3600); // TTL in seconds
|
|
179
|
+
const value = await platform.cache.get<User>('key');
|
|
180
|
+
await platform.cache.delete('key');
|
|
181
|
+
|
|
182
|
+
// Batch operations
|
|
183
|
+
const values = await platform.cache.mget(['key1', 'key2', 'key3']);
|
|
184
|
+
await platform.cache.mset([
|
|
185
|
+
{ key: 'a', value: 1 },
|
|
186
|
+
{ key: 'b', value: 2, ttl: 60 },
|
|
187
|
+
]);
|
|
188
|
+
|
|
189
|
+
// Increment/Decrement
|
|
190
|
+
const count = await platform.cache.incr('counter');
|
|
191
|
+
|
|
192
|
+
// Pattern delete
|
|
193
|
+
await platform.cache.deletePattern('user:*');
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Storage
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// Upload
|
|
200
|
+
const file = await platform.storage.upload('path/to/file.txt', buffer, {
|
|
201
|
+
contentType: 'text/plain',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
// Download
|
|
205
|
+
const data = await platform.storage.download('path/to/file.txt');
|
|
206
|
+
|
|
207
|
+
// Get signed URL
|
|
208
|
+
const url = await platform.storage.getSignedUrl('path/to/file.txt', 3600);
|
|
209
|
+
|
|
210
|
+
// Delete
|
|
211
|
+
await platform.storage.delete('path/to/file.txt');
|
|
212
|
+
|
|
213
|
+
// Check existence
|
|
214
|
+
const exists = await platform.storage.exists('path/to/file.txt');
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Email
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// Send email
|
|
221
|
+
const result = await platform.email.send({
|
|
222
|
+
to: 'user@example.com',
|
|
223
|
+
from: 'noreply@yourdomain.com',
|
|
224
|
+
subject: 'Hello',
|
|
225
|
+
text: 'Plain text body',
|
|
226
|
+
html: '<h1>HTML body</h1>',
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Batch send
|
|
230
|
+
const results = await platform.email.sendBatch([
|
|
231
|
+
{ to: 'a@example.com', subject: 'A', text: 'A' },
|
|
232
|
+
{ to: 'b@example.com', subject: 'B', text: 'B' },
|
|
233
|
+
]);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Queue
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
// Add job
|
|
240
|
+
const job = await platform.queue.add('sendEmail', {
|
|
241
|
+
userId: '123',
|
|
242
|
+
template: 'welcome',
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
// Process jobs
|
|
246
|
+
platform.queue.process(async (job) => {
|
|
247
|
+
console.log('Processing:', job.name, job.data);
|
|
248
|
+
return { success: true };
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Get job status
|
|
252
|
+
const job = await platform.queue.getJob('job_id');
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Health Checks
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const health = await platform.healthCheck();
|
|
259
|
+
|
|
260
|
+
console.log(health);
|
|
261
|
+
// {
|
|
262
|
+
// healthy: true,
|
|
263
|
+
// services: {
|
|
264
|
+
// database: true,
|
|
265
|
+
// cache: true,
|
|
266
|
+
// storage: true,
|
|
267
|
+
// email: true,
|
|
268
|
+
// queue: true,
|
|
269
|
+
// },
|
|
270
|
+
// timestamp: 1701234567890,
|
|
271
|
+
// }
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
## Testing
|
|
275
|
+
|
|
276
|
+
Use memory adapters for fast, isolated tests:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { createPlatform, MemoryDatabase, MemoryCache } from '@digilogiclabs/platform-core';
|
|
280
|
+
|
|
281
|
+
describe('MyService', () => {
|
|
282
|
+
const platform = createPlatform(); // Uses memory adapters
|
|
283
|
+
|
|
284
|
+
beforeEach(async () => {
|
|
285
|
+
// Reset state between tests
|
|
286
|
+
await platform.close();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should create user', async () => {
|
|
290
|
+
const result = await platform.db
|
|
291
|
+
.from('users')
|
|
292
|
+
.insert({ name: 'Test' })
|
|
293
|
+
.execute();
|
|
294
|
+
|
|
295
|
+
expect(result.data).toHaveLength(1);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Direct Adapter Usage
|
|
301
|
+
|
|
302
|
+
You can also use adapters directly:
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import {
|
|
306
|
+
MemoryDatabase,
|
|
307
|
+
MemoryCache,
|
|
308
|
+
SupabaseDatabase,
|
|
309
|
+
UpstashCache,
|
|
310
|
+
S3Storage,
|
|
311
|
+
ResendEmail,
|
|
312
|
+
ConsoleEmail,
|
|
313
|
+
} from '@digilogiclabs/platform-core';
|
|
314
|
+
|
|
315
|
+
// Create adapters directly
|
|
316
|
+
const db = new MemoryDatabase();
|
|
317
|
+
const cache = new MemoryCache();
|
|
318
|
+
const email = new ConsoleEmail(); // Logs to console
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Migration from Direct Provider Usage
|
|
322
|
+
|
|
323
|
+
### Before (Direct Supabase)
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { createClient } from '@supabase/supabase-js';
|
|
327
|
+
|
|
328
|
+
const supabase = createClient(url, key);
|
|
329
|
+
const { data } = await supabase.from('users').select('*').eq('active', true);
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
### After (Platform Core)
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import { createPlatformAsync } from '@digilogiclabs/platform-core';
|
|
336
|
+
|
|
337
|
+
const platform = await createPlatformAsync();
|
|
338
|
+
const result = await platform.db.from('users').where('active', '=', true).execute();
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Enterprise Patterns
|
|
342
|
+
|
|
343
|
+
### Middleware
|
|
344
|
+
|
|
345
|
+
Compose middleware for cross-cutting concerns:
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
import { createMiddlewareChain, createLoggingMiddleware, createMetricsMiddleware } from '@digilogiclabs/platform-core';
|
|
349
|
+
|
|
350
|
+
const chain = createMiddlewareChain()
|
|
351
|
+
.use(createLoggingMiddleware(logger))
|
|
352
|
+
.use(createMetricsMiddleware(metrics))
|
|
353
|
+
.use({
|
|
354
|
+
name: 'custom',
|
|
355
|
+
before: (ctx) => console.log('Before:', ctx.operation),
|
|
356
|
+
after: (ctx) => console.log('After:', ctx.operation, ctx.duration + 'ms'),
|
|
357
|
+
onError: (ctx, error) => console.error('Error:', error),
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Execute with middleware
|
|
361
|
+
const result = await chain.execute(
|
|
362
|
+
{ service: 'database', operation: 'query', args: { table: 'users' }, logger, startTime: Date.now(), correlationId: 'abc' },
|
|
363
|
+
async () => db.from('users').execute()
|
|
364
|
+
);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Hooks
|
|
368
|
+
|
|
369
|
+
Register lifecycle hooks for platform events:
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
import { createHookRegistry } from '@digilogiclabs/platform-core';
|
|
373
|
+
|
|
374
|
+
const hooks = createHookRegistry(logger);
|
|
375
|
+
|
|
376
|
+
hooks.register({
|
|
377
|
+
onReady: () => console.log('Platform ready'),
|
|
378
|
+
onShutdown: () => console.log('Shutting down'),
|
|
379
|
+
beforeQuery: ({ table, operation }) => console.log(`Query: ${operation} on ${table}`),
|
|
380
|
+
afterQuery: ({ table, duration }) => console.log(`Query completed in ${duration}ms`),
|
|
381
|
+
onCacheHit: (key, value) => console.log(`Cache hit: ${key}`),
|
|
382
|
+
onCacheMiss: (key) => console.log(`Cache miss: ${key}`),
|
|
383
|
+
onError: ({ service, operation, error }) => console.error(`Error in ${service}.${operation}:`, error),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// Execute hooks
|
|
387
|
+
await hooks.execute('onReady');
|
|
388
|
+
await hooks.execute('beforeQuery', { table: 'users', operation: 'select' });
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Resilience Patterns
|
|
392
|
+
|
|
393
|
+
#### Retry with Exponential Backoff
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
import { withRetry, RetryConfigs, RetryPredicates } from '@digilogiclabs/platform-core';
|
|
397
|
+
|
|
398
|
+
const result = await withRetry(
|
|
399
|
+
() => fetchData(),
|
|
400
|
+
{
|
|
401
|
+
maxAttempts: 3,
|
|
402
|
+
baseDelay: 100,
|
|
403
|
+
maxDelay: 5000,
|
|
404
|
+
backoffMultiplier: 2,
|
|
405
|
+
shouldRetry: RetryPredicates.networkErrors,
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
// Or use presets
|
|
410
|
+
const result = await withRetry(() => fetchData(), RetryConfigs.standard);
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
#### Circuit Breaker
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
import { CircuitBreaker, CircuitBreakerRegistry } from '@digilogiclabs/platform-core';
|
|
417
|
+
|
|
418
|
+
const breaker = new CircuitBreaker({
|
|
419
|
+
failureThreshold: 5,
|
|
420
|
+
resetTimeout: 30000,
|
|
421
|
+
halfOpenRequests: 3,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
const result = await breaker.execute(() => externalApiCall());
|
|
425
|
+
|
|
426
|
+
// Or use a registry for named breakers
|
|
427
|
+
const registry = new CircuitBreakerRegistry();
|
|
428
|
+
const apiBreaker = registry.get('external-api');
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
#### Timeout
|
|
432
|
+
|
|
433
|
+
```typescript
|
|
434
|
+
import { withTimeout, TimeoutError } from '@digilogiclabs/platform-core';
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
const result = await withTimeout(() => slowOperation(), 5000);
|
|
438
|
+
} catch (error) {
|
|
439
|
+
if (error instanceof TimeoutError) {
|
|
440
|
+
console.log('Operation timed out');
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
#### Bulkhead (Concurrency Isolation)
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
import { Bulkhead } from '@digilogiclabs/platform-core';
|
|
449
|
+
|
|
450
|
+
const bulkhead = new Bulkhead({
|
|
451
|
+
maxConcurrent: 10,
|
|
452
|
+
maxQueued: 100,
|
|
453
|
+
timeout: 30000,
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const result = await bulkhead.execute(() => cpuIntensiveTask());
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### Fallback
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
import { withFallback, FallbackStrategies } from '@digilogiclabs/platform-core';
|
|
463
|
+
|
|
464
|
+
// Static fallback
|
|
465
|
+
const result = await withFallback(
|
|
466
|
+
() => fetchFromApi(),
|
|
467
|
+
FallbackStrategies.value({ cached: true, data: [] })
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
// Dynamic fallback
|
|
471
|
+
const result = await withFallback(
|
|
472
|
+
() => fetchFromPrimary(),
|
|
473
|
+
FallbackStrategies.execute(() => fetchFromSecondary())
|
|
474
|
+
);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Metrics
|
|
478
|
+
|
|
479
|
+
Track application metrics with a provider-agnostic interface:
|
|
480
|
+
|
|
481
|
+
```typescript
|
|
482
|
+
import { MemoryMetrics, createScopedMetrics } from '@digilogiclabs/platform-core';
|
|
483
|
+
|
|
484
|
+
const metrics = new MemoryMetrics();
|
|
485
|
+
|
|
486
|
+
// Counter
|
|
487
|
+
metrics.increment('api.requests', 1, { method: 'GET', path: '/users' });
|
|
488
|
+
metrics.decrement('active.connections');
|
|
489
|
+
|
|
490
|
+
// Gauge
|
|
491
|
+
metrics.gauge('queue.size', 42, { queue: 'emails' });
|
|
492
|
+
|
|
493
|
+
// Histogram
|
|
494
|
+
metrics.histogram('response.size', 1024, { endpoint: '/api/users' });
|
|
495
|
+
|
|
496
|
+
// Timer
|
|
497
|
+
const stopTimer = metrics.startTimer('api.request.duration', { method: 'POST' });
|
|
498
|
+
// ... do work ...
|
|
499
|
+
stopTimer(); // Records duration
|
|
500
|
+
|
|
501
|
+
// Timing
|
|
502
|
+
metrics.timing('db.query.duration', 42, { table: 'users' });
|
|
503
|
+
|
|
504
|
+
// Scoped metrics with prefix
|
|
505
|
+
const dbMetrics = createScopedMetrics(metrics, 'database', { service: 'users' });
|
|
506
|
+
dbMetrics.timing('query', 50); // Records as 'database.query' with service=users tag
|
|
507
|
+
|
|
508
|
+
// Get summary (for testing)
|
|
509
|
+
const summary = metrics.getSummary();
|
|
510
|
+
console.log(summary.counters, summary.timings);
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Local Development
|
|
514
|
+
|
|
515
|
+
Start the development infrastructure:
|
|
516
|
+
|
|
517
|
+
```bash
|
|
518
|
+
# Start core services (PostgreSQL, Redis, MinIO, MailHog)
|
|
519
|
+
./infrastructure/scripts/dev.sh start
|
|
520
|
+
|
|
521
|
+
# Start with observability (adds Prometheus, Grafana)
|
|
522
|
+
./infrastructure/scripts/dev.sh start:obs
|
|
523
|
+
|
|
524
|
+
# Start with dev tools (adds PgAdmin, RedisInsight)
|
|
525
|
+
./infrastructure/scripts/dev.sh start:tools
|
|
526
|
+
|
|
527
|
+
# Check service health
|
|
528
|
+
./infrastructure/scripts/dev.sh health
|
|
529
|
+
|
|
530
|
+
# Connect to databases
|
|
531
|
+
./infrastructure/scripts/dev.sh psql
|
|
532
|
+
./infrastructure/scripts/dev.sh redis-cli
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
Service URLs:
|
|
536
|
+
- PostgreSQL: `localhost:5432` (user: dll, pass: development)
|
|
537
|
+
- Redis: `localhost:6379` (pass: development)
|
|
538
|
+
- MinIO: `localhost:9000` (console: 9001)
|
|
539
|
+
- MailHog: `localhost:8025` (SMTP: 1025)
|
|
540
|
+
- Prometheus: `localhost:9090` (with observability profile)
|
|
541
|
+
- Grafana: `localhost:3001` (with observability profile)
|
|
542
|
+
|
|
543
|
+
## License
|
|
544
|
+
|
|
545
|
+
MIT
|