@xbg.solutions/create-backend 1.0.0 → 1.0.2

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.
@@ -0,0 +1,527 @@
1
+ ---
2
+ description: "Utilities in the XBG boilerplate backend: logger with PII sanitization, AES-256-GCM hashing, token handler with blacklisting, multi-level cache, and all communication connectors (email, SMS, push, CRM, LLM, etc.)."
3
+ ---
4
+
5
+ # XBG Boilerplate Backend — Utilities
6
+
7
+ Each utility is a standalone npm package under `@xbg.solutions/utils-*`. Install only what you need.
8
+
9
+ ---
10
+
11
+ ## Logger
12
+
13
+ **Package:** `@xbg.solutions/utils-logger`
14
+
15
+ Structured JSON logger with automatic PII sanitization and correlation ID tracking.
16
+
17
+ ### Import and Basic Use
18
+
19
+ ```typescript
20
+ import { logger } from '@xbg.solutions/utils-logger';
21
+
22
+ logger.debug('Detailed trace', { userId, queryOptions });
23
+ logger.info('Operation completed', { userId, entityId: product.id });
24
+ logger.warn('Retrying after failure', { attempt: 2, error: err.message });
25
+ logger.error('Operation failed', err, { requestId, userId });
26
+ // ^^^ Error object is second param for error()
27
+ ```
28
+
29
+ ### PII Sanitization — Automatic
30
+
31
+ The logger automatically redacts these field names from logged objects:
32
+ - `password`, `passwordHash`, `secret`, `token`, `apiKey`, `authorization`
33
+ - `email`, `phone`, `ssn`, `creditCard`, `cvv`
34
+ - `firstName`, `lastName`, `address`
35
+
36
+ ```typescript
37
+ // This is safe to log — email will be redacted to '[REDACTED]'
38
+ logger.info('User created', {
39
+ userId: 'uid-123',
40
+ email: 'user@example.com', // ← becomes '[REDACTED]' in output
41
+ name: 'John Doe', // ← also redacted (firstName/lastName components)
42
+ });
43
+ ```
44
+
45
+ ### Structured Logging Fields
46
+
47
+ Always include `requestId` for correlation across log lines:
48
+
49
+ ```typescript
50
+ logger.info('Product updated', {
51
+ requestId: context.requestId,
52
+ userId: context.userId,
53
+ productId: product.id,
54
+ priceChange: { old: existing.price, new: product.price },
55
+ });
56
+ ```
57
+
58
+ ### Request-Scoped Logger
59
+
60
+ The logging middleware attaches a logger to `req` with the requestId pre-set:
61
+
62
+ ```typescript
63
+ // In a controller (if needed):
64
+ import { Request } from 'express';
65
+ const reqLogger = (req as any).logger ?? logger;
66
+ reqLogger.info('Custom log', { data });
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Hashing — PII Encryption (AES-256-GCM)
72
+
73
+ **Package:** `@xbg.solutions/utils-hashing`
74
+
75
+ Provides **reversible** AES-256-GCM encryption for PII fields. This is encryption, not one-way hashing — the name `hashFields` is the API surface, but the data can be decrypted.
76
+
77
+ ### Setup
78
+
79
+ ```bash
80
+ # Generate encryption key (64 hex chars = 32 bytes for AES-256)
81
+ openssl rand -hex 32
82
+ # Add to .env:
83
+ PII_ENCRYPTION_KEY=your-64-char-hex-key
84
+ ```
85
+
86
+ ### Encrypting Fields
87
+
88
+ ```typescript
89
+ import { hashValue, hashFields } from '@xbg.solutions/utils-hashing';
90
+
91
+ // Encrypt a single value
92
+ const encrypted = hashValue('user@example.com');
93
+ // Format: "base64iv:base64ciphertext:base64authtag"
94
+
95
+ // Encrypt specific PII fields on a data object (uses hashed-fields-lookup config)
96
+ const encryptedUserData = hashFields(userData, 'user');
97
+ ```
98
+
99
+ ### Decrypting Fields
100
+
101
+ ```typescript
102
+ import { unhashValue, unhashFields } from '@xbg.solutions/utils-hashing';
103
+
104
+ // Decrypt a single value
105
+ const plaintext = unhashValue(encrypted);
106
+
107
+ // Decrypt specific fields on a retrieved entity
108
+ const decryptedUser = unhashFields(user, ['user.email', 'user.phone']);
109
+ ```
110
+
111
+ ### Which Fields Are Hashed
112
+
113
+ Configured in the hashing package's hashed-fields-lookup. To check:
114
+
115
+ ```typescript
116
+ import { isHashedField } from '@xbg.solutions/utils-hashing';
117
+
118
+ // Check if a field path should be hashed:
119
+ isHashedField('user.email') // → true
120
+ isHashedField('user.phoneNumber') // → true
121
+ isHashedField('user.name') // → false
122
+ isHashedField('product.name') // → false
123
+ ```
124
+
125
+ ### Anti-Examples
126
+
127
+ ```typescript
128
+ // ❌ Don't decrypt eagerly — only decrypt when you need plaintext
129
+ const users = await userRepo.findAll({});
130
+ const decrypted = users.map(u => unhashFields(u, ['user.email'])); // ← only if you're sending PII to client
131
+
132
+ // ✅ Keep encrypted in storage, decrypt only at response boundary
133
+ const user = await userRepo.findById(id);
134
+ if (sendingToClient) {
135
+ return unhashFields(user, ['user.email', 'user.phone']);
136
+ }
137
+
138
+ // ❌ Don't compare encrypted values directly
139
+ if (user.email === 'test@example.com') { ... } // always false — it's encrypted
140
+
141
+ // ✅ Encrypt the search value first, or use a separate search index
142
+ const encryptedEmail = hashValue('test@example.com');
143
+ const users = await userRepo.findAll({ where: [{ field: 'email', operator: '==', value: encryptedEmail }] });
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Token Handler — JWT & Blacklisting
149
+
150
+ **Package:** `@xbg.solutions/utils-token-handler`
151
+
152
+ Wraps Firebase Auth token verification with blacklisting support and normalized token shape.
153
+
154
+ ### Basic Verification
155
+
156
+ ```typescript
157
+ import { tokenHandler } from '@xbg.solutions/utils-token-handler';
158
+ import { logger } from '@xbg.solutions/utils-logger';
159
+
160
+ const result = await tokenHandler.verifyAndUnpack(bearerToken, logger);
161
+ if (!result.isValid) {
162
+ // result.error contains reason, result.isBlacklisted indicates revocation
163
+ return res.status(401).json({ error: result.error });
164
+ }
165
+
166
+ // Normalized token
167
+ const { authUID, userUID, email, customClaims } = result.token!;
168
+ ```
169
+
170
+ ### Token Blacklisting
171
+
172
+ ```typescript
173
+ import { tokenHandler } from '@xbg.solutions/utils-token-handler';
174
+ import { logger } from '@xbg.solutions/utils-logger';
175
+
176
+ // Blacklist a single token (e.g., on logout)
177
+ const tokenId = await tokenHandler.getTokenIdentifier(rawToken);
178
+ await tokenHandler.blacklistToken(tokenId, authUID, 'LOGOUT', tokenExpiresAt, null, logger);
179
+
180
+ // Blacklist all tokens for a user (e.g., password change, account compromise)
181
+ await tokenHandler.blacklistAllUserTokens(authUID, 'PASSWORD_CHANGE', null, logger);
182
+ ```
183
+
184
+ Blacklisted tokens are checked on every `verifyAndUnpack()` call. Firebase's own revocation list is also checked (`checkRevoked: true`). Cleanup of expired blacklist entries runs on schedule.
185
+
186
+ ### Normalized Token Shape
187
+
188
+ ```typescript
189
+ interface NormalizedToken<TCustomClaims> {
190
+ authUID: string; // Firebase Auth UID
191
+ userUID: string | null; // App-specific user ID (from custom claims)
192
+ email: string | null;
193
+ emailVerified: boolean;
194
+ phoneNumber: string | null;
195
+ issuedAt: number; // Unix timestamp
196
+ expiresAt: number;
197
+ issuer: string;
198
+ customClaims: TCustomClaims;
199
+ rawClaims: Record<string, any>;
200
+ }
201
+ ```
202
+
203
+ ### API Key Auth (Service-to-Service)
204
+
205
+ ```typescript
206
+ import { requireApiKey } from '@xbg.solutions/backend-core';
207
+
208
+ // In controller routes:
209
+ this.router.post(
210
+ '/webhook',
211
+ requireApiKey([process.env.WEBHOOK_SECRET!]),
212
+ this.handleWebhook.bind(this)
213
+ );
214
+ ```
215
+
216
+ ---
217
+
218
+ ## Cache Connector
219
+
220
+ **Package:** `@xbg.solutions/utils-cache-connector`
221
+
222
+ Progressive multi-level cache. Opt-in per repository. Global kill switch.
223
+
224
+ ### Providers
225
+
226
+ | Provider | Latency | Scope | Use for |
227
+ |---|---|---|---|
228
+ | `memory` | ~1ms | Single function instance | Hot data, auth tokens |
229
+ | `firestore` | ~50-100ms | All instances | Shared config, user sessions |
230
+ | `redis` | ~5ms | All instances | High-traffic data (requires Redis) |
231
+ | `noop` | 0ms | None | Default when cache disabled |
232
+
233
+ ### Configuration
234
+
235
+ ```bash
236
+ # .env
237
+ CACHE_ENABLED=true
238
+ CACHE_DEFAULT_PROVIDER=memory
239
+ CACHE_DEFAULT_TTL=300
240
+ CACHE_NAMESPACE=myapp
241
+
242
+ # For Redis:
243
+ CACHE_REDIS_HOST=localhost
244
+ CACHE_REDIS_PORT=6379
245
+ ```
246
+
247
+ ### Using in Repositories
248
+
249
+ ```typescript
250
+ import { BaseRepository } from '@xbg.solutions/backend-core';
251
+
252
+ export class SessionRepository extends BaseRepository<Session> {
253
+ protected collectionName = 'sessions';
254
+
255
+ protected cacheConfig = {
256
+ enabled: true,
257
+ provider: 'memory' as const,
258
+ ttl: 900, // 15 minutes — matches JWT expiry
259
+ keyPrefix: 'session',
260
+ tags: ['sessions'],
261
+ };
262
+
263
+ protected fromFirestore(id: string, data: DocumentData): Session { ... }
264
+ }
265
+
266
+ // Usage
267
+ const session = await sessionRepo.findByIdCached('sess-123');
268
+ const fresh = await sessionRepo.findByIdCached('sess-123', { forceRefresh: true, ttl: 60 });
269
+ ```
270
+
271
+ ### Direct Cache Access (Advanced)
272
+
273
+ ```typescript
274
+ import { getCacheConnector } from '@xbg.solutions/utils-cache-connector';
275
+
276
+ const cache = getCacheConnector();
277
+
278
+ // Set
279
+ await cache.set('my-key', { data: 'value' }, { ttl: 300, provider: 'memory' });
280
+
281
+ // Get
282
+ const value = await cache.get<MyType>('my-key', { provider: 'memory' });
283
+
284
+ // Delete
285
+ await cache.delete('my-key', { provider: 'memory' });
286
+
287
+ // Invalidate by tag (invalidates all keys with this tag)
288
+ await cache.invalidateByTags(['sessions'], { provider: 'memory' });
289
+ ```
290
+
291
+ ---
292
+
293
+ ## Communication Connectors
294
+
295
+ All connectors follow the same pattern: import the connector singleton from its `@xbg.solutions/utils-*` package, call methods, providers handle the transport.
296
+
297
+ ### Email Connector
298
+
299
+ **Package:** `@xbg.solutions/utils-email-connector`
300
+
301
+ Providers: Mailjet (implemented), Ortto, SendGrid, AWS SES (stub/planned)
302
+
303
+ ```typescript
304
+ import { emailConnector } from '@xbg.solutions/utils-email-connector';
305
+
306
+ // Simple email
307
+ await emailConnector.sendEmail({
308
+ to: 'user@example.com',
309
+ subject: 'Welcome to the platform',
310
+ html: '<h1>Welcome!</h1><p>Your account is ready.</p>',
311
+ text: 'Welcome! Your account is ready.',
312
+ });
313
+
314
+ // With sender override
315
+ await emailConnector.sendEmail({
316
+ to: 'user@example.com',
317
+ from: { email: 'noreply@myapp.com', name: 'MyApp' },
318
+ subject: 'Password reset',
319
+ html: '<p>Click <a href="...">here</a> to reset.</p>',
320
+ });
321
+ ```
322
+
323
+ Configure in `.env`:
324
+ ```bash
325
+ EMAIL_PROVIDER=mailjet # mailjet | sendgrid | ses
326
+ MAILJET_API_KEY=...
327
+ MAILJET_SECRET_KEY=...
328
+ EMAIL_FROM_ADDRESS=noreply@myapp.com
329
+ EMAIL_FROM_NAME=MyApp
330
+ ```
331
+
332
+ ### SMS Connector
333
+
334
+ **Package:** `@xbg.solutions/utils-sms-connector`
335
+
336
+ Providers: Twilio (implemented), MessageBird, AWS SNS (stub/planned)
337
+
338
+ ```typescript
339
+ import { smsConnector } from '@xbg.solutions/utils-sms-connector';
340
+
341
+ await smsConnector.sendMessage({
342
+ to: '+12025551234',
343
+ body: 'Your verification code is: 847293',
344
+ });
345
+ ```
346
+
347
+ Configure:
348
+ ```bash
349
+ SMS_PROVIDER=twilio
350
+ TWILIO_ACCOUNT_SID=...
351
+ TWILIO_AUTH_TOKEN=...
352
+ TWILIO_FROM_NUMBER=+15005550006
353
+ ```
354
+
355
+ ### Push Notifications Connector
356
+
357
+ **Package:** `@xbg.solutions/utils-push-notifications-connector`
358
+
359
+ Provider: Firebase Cloud Messaging (FCM)
360
+
361
+ ```typescript
362
+ import { pushNotificationsConnector } from '@xbg.solutions/utils-push-notifications-connector';
363
+
364
+ // Single device
365
+ await pushNotificationsConnector.send({
366
+ token: deviceFcmToken,
367
+ notification: {
368
+ title: 'New Message',
369
+ body: 'You have a new message from Alice',
370
+ },
371
+ data: { messageId: 'msg-123', type: 'chat' },
372
+ });
373
+
374
+ // Multiple devices
375
+ await pushNotificationsConnector.sendMulticast({
376
+ tokens: [token1, token2, token3],
377
+ notification: { title: 'System update', body: 'Maintenance in 1 hour' },
378
+ });
379
+ ```
380
+
381
+ ### CRM Connector
382
+
383
+ **Package:** `@xbg.solutions/utils-crm-connector`
384
+
385
+ Providers: HubSpot (implemented), Salesforce, Attio (stub/planned)
386
+
387
+ ```typescript
388
+ import { crmConnector } from '@xbg.solutions/utils-crm-connector';
389
+
390
+ // Create contact
391
+ const contact = await crmConnector.createContact({
392
+ email: 'lead@company.com',
393
+ firstName: 'Jane',
394
+ lastName: 'Smith',
395
+ company: 'Acme Corp',
396
+ phone: '+12025551234',
397
+ });
398
+
399
+ // Update contact
400
+ await crmConnector.updateContact(contact.id, {
401
+ lifecycleStage: 'customer',
402
+ });
403
+
404
+ // Create deal
405
+ await crmConnector.createDeal({
406
+ name: 'Acme Corp - Enterprise',
407
+ amount: 50000,
408
+ contactId: contact.id,
409
+ });
410
+ ```
411
+
412
+ Configure:
413
+ ```bash
414
+ CRM_PROVIDER=hubspot
415
+ HUBSPOT_ACCESS_TOKEN=...
416
+ ```
417
+
418
+ ### LLM Connector
419
+
420
+ **Package:** `@xbg.solutions/utils-llm-connector`
421
+
422
+ Providers: Claude/Anthropic, OpenAI, Gemini
423
+
424
+ ```typescript
425
+ import { llmConnector } from '@xbg.solutions/utils-llm-connector';
426
+
427
+ const response = await llmConnector.complete({
428
+ messages: [
429
+ { role: 'user', content: 'Summarize this product description: ...' }
430
+ ],
431
+ maxTokens: 200,
432
+ });
433
+
434
+ // response.content: string
435
+ // response.usage: { inputTokens, outputTokens }
436
+ ```
437
+
438
+ Configure:
439
+ ```bash
440
+ LLM_PROVIDER=anthropic # anthropic | openai | gemini
441
+ ANTHROPIC_API_KEY=...
442
+ ANTHROPIC_MODEL=claude-sonnet-4-6
443
+ ```
444
+
445
+ ### Other Connectors
446
+
447
+ | Package | Description |
448
+ |---|---|
449
+ | `@xbg.solutions/utils-document-connector` | E-signature (PandaDoc) |
450
+ | `@xbg.solutions/utils-journey-connector` | Marketing automation (Ortto) |
451
+ | `@xbg.solutions/utils-survey-connector` | Surveys (Typeform) |
452
+ | `@xbg.solutions/utils-work-mgmt-connector` | Tasks (ClickUp/Notion) |
453
+ | `@xbg.solutions/utils-erp-connector` | HR/Finance (Workday) |
454
+ | `@xbg.solutions/utils-address-validation` | Google Maps validation |
455
+ | `@xbg.solutions/utils-realtime-connector` | Firebase Realtime DB |
456
+ | `@xbg.solutions/utils-firebase-event-bridge` | Firebase → internal events |
457
+ | `@xbg.solutions/utils-firestore-connector` | Multi-DB Firestore setup |
458
+ | `@xbg.solutions/utils-timezone` | Timezone conversion utils |
459
+
460
+ ---
461
+
462
+ ## Subscribing to Events for Communication Side Effects
463
+
464
+ Register subscribers in your project's `src/subscribers/` directory:
465
+
466
+ ```typescript
467
+ import { eventBus, EventType, UserCreatedPayload } from '@xbg.solutions/utils-events';
468
+ import { emailConnector } from '@xbg.solutions/utils-email-connector';
469
+
470
+ export function registerCommunicationSubscribers(): void {
471
+ eventBus.subscribe<UserCreatedPayload>(
472
+ EventType.USER_CREATED,
473
+ async ({ userUID, accountUID }) => {
474
+ await emailConnector.sendEmail({
475
+ to: '...', // fetch user email from repo
476
+ subject: 'Welcome!',
477
+ html: '<p>Your account is ready.</p>',
478
+ });
479
+ }
480
+ );
481
+ }
482
+ ```
483
+
484
+ Import and call `registerCommunicationSubscribers()` in your `src/index.ts` or `app.ts` at startup.
485
+
486
+ ---
487
+
488
+ ## Anti-Examples
489
+
490
+ ```typescript
491
+ // ❌ Don't import logger from wrong path
492
+ import { logger } from '../../logger'; // wrong — use the package
493
+ import { Logger } from './logger'; // creates new instance, wrong
494
+
495
+ // ✅ Always import the singleton from the package
496
+ import { logger } from '@xbg.solutions/utils-logger';
497
+
498
+ // ❌ Don't log raw PII
499
+ logger.info('User login', { email: user.email }); // ← logs plaintext email to Cloud Logging
500
+
501
+ // ✅ Log identifiers, not PII values
502
+ logger.info('User login', { userId: user.id });
503
+
504
+ // ❌ Don't call connectors without checking feature flags
505
+ await emailConnector.send({ ... });
506
+
507
+ // ✅ Check feature is enabled
508
+ import { isFeatureEnabled } from '@xbg.solutions/backend-core';
509
+ if (isFeatureEnabled('notifications')) {
510
+ await emailConnector.send({ ... });
511
+ }
512
+
513
+ // ❌ Don't block on communication failures
514
+ try {
515
+ await smsConnector.sendMessage({ ... });
516
+ } catch (e) {
517
+ throw e; // ← don't let SMS failure break the user flow
518
+ }
519
+
520
+ // ✅ Log and continue
521
+ try {
522
+ await smsConnector.sendMessage({ ... });
523
+ } catch (e) {
524
+ logger.warn('SMS send failed', { error: e, userId });
525
+ // Continue — SMS is non-critical
526
+ }
527
+ ```
@@ -0,0 +1,30 @@
1
+ ---
2
+ description: "Root skill index for XBG agentic builder libraries. Routes to the appropriate sub-skill based on the library and topic."
3
+ ---
4
+
5
+ # XBG Agentic Builder Libraries
6
+
7
+ This repository contains libraries designed to accelerate AI-assisted application development. Each library has its own skill tree with detailed guidance for Claude.
8
+
9
+ ---
10
+
11
+ ## Available Libraries
12
+
13
+ | Library | Skill Path | Description |
14
+ |---|---|---|
15
+ | **Boilerplate Backend (bpbe)** | `bpbe/` | Production-ready Node.js/TypeScript backend on Firebase Functions + Firestore + Express.js |
16
+
17
+ ---
18
+
19
+ ## Boilerplate Backend — Quick Navigation
20
+
21
+ A 3-layer (Controller → Service → Repository) backend distributed as `@xbg.solutions/*` npm packages. AI generates business logic from declarative data models.
22
+
23
+ | Topic | Skill | Use when you need to... |
24
+ |---|---|---|
25
+ | Overview | `bpbe/skill.md` | Understand the architecture, philosophy, and how the pieces fit together |
26
+ | Setup | `bpbe/setup/skill.md` | Create a new project, configure .env, npm scripts, Firebase config, validation |
27
+ | Data | `bpbe/data/skill.md` | Define entities, repositories, DataModelSpecification, code generator, Firestore patterns |
28
+ | Services | `bpbe/services/skill.md` | Business logic, lifecycle hooks, access control, events, auth patterns |
29
+ | Utilities | `bpbe/utils/skill.md` | Logger, PII hashing, token handler, cache, email/SMS/push/CRM/LLM connectors |
30
+ | API | `bpbe/api/skill.md` | Controllers, routes, middleware, response shapes, wiring up index.ts |
@@ -4,7 +4,7 @@
4
4
  * Code Generation Script
5
5
  * Generates entities, repositories, services, and controllers from data model specifications
6
6
  *
7
- * Uses the code generator from @xbg/backend-core
7
+ * Uses the code generator from @xbg.solutions/backend-core
8
8
  */
9
9
 
10
10
  const path = require('path');
@@ -28,12 +28,12 @@ ${colors.bright}${colors.blue}╔═══════════════
28
28
  ╚════════════════════════════════════════════════════════════╝${colors.reset}
29
29
  `);
30
30
 
31
- // Import generator from @xbg/backend-core package
31
+ // Import generator from @xbg.solutions/backend-core package
32
32
  let createGenerator;
33
33
  try {
34
- ({ createGenerator } = require('@xbg/backend-core'));
34
+ ({ createGenerator } = require('@xbg.solutions/backend-core'));
35
35
  } catch (error) {
36
- console.error(`${colors.red}❌ @xbg/backend-core not installed. Run 'npm install' first.${colors.reset}`);
36
+ console.error(`${colors.red}❌ @xbg.solutions/backend-core not installed. Run 'npm install' first.${colors.reset}`);
37
37
  process.exit(1);
38
38
  }
39
39
 
@@ -80,7 +80,7 @@ try {
80
80
  });
81
81
  console.log('');
82
82
 
83
- // Create generator - templates come from @xbg/backend-core package
83
+ // Create generator - templates come from @xbg.solutions/backend-core package
84
84
  const outputDir = path.join(__dirname, '../functions/src/generated');
85
85
  const generator = createGenerator(outputDir);
86
86
 
@@ -4,8 +4,8 @@
4
4
  */
5
5
 
6
6
  import * as functions from 'firebase-functions';
7
- import { createApp } from '@xbg/backend-core';
8
- import { logger } from '@xbg/utils-logger';
7
+ import { createApp } from '@xbg.solutions/backend-core';
8
+ import { logger } from '@xbg.solutions/utils-logger';
9
9
 
10
10
  // Import controllers here as they are created
11
11
  // import { UserController } from './generated/controllers/UserController';