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