@carlonicora/nestjs-neo4jsonapi 1.34.0 → 1.34.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @carlonicora/nestjs-neo4jsonapi
2
2
 
3
- A comprehensive NestJS foundation package providing JSON:API compliant APIs, Neo4j graph database integration, Redis caching, LangChain-based AI agents (including GraphRAG), and common utilities for building modern multi-tenant applications.
3
+ A comprehensive NestJS foundation package providing JSON:API compliant APIs, Neo4j graph database integration, Redis caching, LangChain-based AI agents (including GraphRAG and DRIFT), OAuth 2.0 server, OpenAPI documentation, and common utilities for building modern multi-tenant applications.
4
4
 
5
5
  ## Table of Contents
6
6
 
@@ -9,14 +9,18 @@ A comprehensive NestJS foundation package providing JSON:API compliant APIs, Neo
9
9
  - [Installation](#installation)
10
10
  - [Environment Variables](#environment-variables)
11
11
  - [Quick Start](#quick-start)
12
- - [Advanced Setup (Custom Bootstrap)](#advanced-setup-custom-bootstrap)
13
12
  - [Company-User Model (B2B & B2C)](#company-user-model-b2b--b2c)
14
13
  - [Required Configuration Files](#required-configuration-files)
15
14
  - [Core Modules](#core-modules)
16
15
  - [Health Check Endpoints](#health-check-endpoints)
17
16
  - [Foundation Modules](#foundation-modules)
18
17
  - [AI Agents](#ai-agents)
18
+ - [DRIFT Module (Advanced Semantic Search)](#drift-module-advanced-semantic-search)
19
+ - [OpenAPI Documentation](#openapi-documentation)
20
+ - [OAuth 2.0 Support](#oauth-20-support)
19
21
  - [Security & Authentication](#security--authentication)
22
+ - [Company Deletion Handler](#company-deletion-handler)
23
+ - [Entity Descriptors (defineEntity)](#entity-descriptors-defineentity)
20
24
  - [Customizing Agent Prompts](#customizing-agent-prompts-optional)
21
25
  - [License](#license)
22
26
 
@@ -27,10 +31,15 @@ A comprehensive NestJS foundation package providing JSON:API compliant APIs, Neo
27
31
  - **Neo4j Integration**: Graph database operations with Cypher query builder
28
32
  - **Redis Caching**: Built-in caching layer with configurable TTLs
29
33
  - **Multi-Tenant Architecture**: Support for both B2B (multi-company) and B2C (single invisible company) scenarios
30
- - **AI Agents**: LangChain-powered agents including GraphRAG for knowledge extraction, summarization, and intelligent responses
34
+ - **AI Agents**: LangChain-powered agents including GraphRAG and DRIFT for knowledge extraction, summarization, and intelligent responses
35
+ - **DRIFT Search**: Advanced semantic search using community detection and HyDE (Hypothetical Document Embedding)
36
+ - **OAuth 2.0 Server**: RFC 6749/7636 compliant authorization server with PKCE support
37
+ - **OpenAPI Documentation**: Auto-generated JSON:API compliant Swagger/Redoc documentation
31
38
  - **Authentication**: JWT-based authentication with role-based access control
32
39
  - **Background Jobs**: BullMQ integration for async job processing
33
40
  - **WebSockets**: Real-time communication support
41
+ - **Vision LLM Support**: Separate configuration for vision/image analysis models
42
+ - **Transcriber Support**: Speech-to-text transcription capabilities
34
43
  - **Tracing**: OpenTelemetry integration for distributed tracing
35
44
  - **Logging**: Structured logging with Loki integration
36
45
 
@@ -49,6 +58,7 @@ The library is designed to run in two modes from the same codebase:
49
58
 
50
59
  - Processes BullMQ jobs asynchronously
51
60
  - Runs scheduled tasks (cron jobs)
61
+ - Discord bot integration (when configured)
52
62
  - No HTTP server - just job processing
53
63
  - Same configuration and modules as API
54
64
 
@@ -66,19 +76,20 @@ pnpm start:prod # API mode
66
76
  pnpm start:worker:prod # Worker mode
67
77
  ```
68
78
 
69
- The mode is determined by the `--mode` flag and configured via `getAppMode()` and `getAppModeConfig()`:
79
+ The mode is determined by the `--mode` flag and configured via `getAppMode()` and `getAppModeConfig()`.
70
80
 
71
81
  ## Architecture
72
82
 
73
- The library is organized into four main layers:
83
+ The library is organized into five main layers:
74
84
 
75
85
  ```
76
86
  @carlonicora/nestjs-neo4jsonapi
77
87
  ├── common/ # Shared utilities, abstracts, decorators, guards
78
88
  ├── config/ # Configuration system and tokens
79
- ├── core/ # Infrastructure modules (18 modules)
80
- ├── foundations/ # Domain/business modules (17 modules)
81
- ├── agents/ # AI agent modules (4 modules)
89
+ ├── core/ # Infrastructure modules (19 modules)
90
+ ├── foundations/ # Domain/business modules (31 modules)
91
+ ├── agents/ # AI agent modules (7 modules)
92
+ ├── openapi/ # OpenAPI/Swagger documentation
82
93
  └── bootstrap/ # Application bootstrap utilities
83
94
  ```
84
95
 
@@ -208,28 +219,84 @@ CACHE_SKIP_PATTERNS=/access,/auth,/notifications,/websocket,/version
208
219
  JWT_SECRET=your-jwt-secret
209
220
  JWT_EXPIRES_IN=1h
210
221
 
222
+ # Auth
223
+ ALLOW_REGISTRATION=true
224
+
225
+ # OAuth 2.0 Server (optional)
226
+ OAUTH_ENABLED=false
227
+ OAUTH_AUTHORIZATION_CODE_LIFETIME=600
228
+ OAUTH_ACCESS_TOKEN_LIFETIME=3600
229
+ OAUTH_REFRESH_TOKEN_LIFETIME=604800
230
+ OAUTH_REQUIRE_PKCE_FOR_PUBLIC_CLIENTS=true
231
+ OAUTH_ROTATE_REFRESH_TOKENS=true
232
+
211
233
  # CORS
212
234
  CORS_ORIGINS=http://localhost:3001
213
235
  CORS_CREDENTIALS=true
236
+ CORS_ORIGIN_PATTERNS=
237
+ CORS_METHODS=GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS
238
+ CORS_ALLOWED_HEADERS=
239
+ CORS_MAX_AGE=86400
240
+ CORS_PREFLIGHT_CONTINUE=false
241
+ CORS_OPTIONS_SUCCESS_STATUS=204
242
+ CORS_LOG_VIOLATIONS=true
214
243
 
215
244
  # AI Configuration (optional)
216
245
  AI_PROVIDER=openai
217
246
  AI_API_KEY=sk-...
218
247
  AI_MODEL=gpt-4o-mini
248
+ AI_URL=
249
+ AI_REGION=
250
+ AI_INSTANCE=
251
+ AI_API_VERSION=
252
+ AI_INPUT_COST_PER_1M_TOKENS=0
253
+ AI_OUTPUT_COST_PER_1M_TOKENS=0
254
+ AI_GOOGLE_CREDENTIALS_BASE64=
255
+
256
+ # Vision LLM (optional - falls back to AI_ settings if not set)
257
+ VISION_PROVIDER=openai
258
+ VISION_API_KEY=
259
+ VISION_MODEL=gpt-4o
260
+ VISION_URL=
261
+ VISION_REGION=
262
+ VISION_SECRET=
263
+ VISION_INSTANCE=
264
+ VISION_API_VERSION=
265
+ VISION_INPUT_COST_PER_1M_TOKENS=0
266
+ VISION_OUTPUT_COST_PER_1M_TOKENS=0
267
+ VISION_GOOGLE_CREDENTIALS_BASE64=
268
+
269
+ # Transcriber (optional)
270
+ TRANSCRIBER_PROVIDER=
271
+ TRANSCRIBER_API_KEY=
272
+ TRANSCRIBER_MODEL=
273
+ TRANSCRIBER_URL=
274
+ TRANSCRIBER_API_VERSION=
219
275
 
220
276
  # Embedder (optional)
221
277
  EMBEDDER_PROVIDER=openrouter
222
278
  EMBEDDER_API_KEY=sk-...
223
279
  EMBEDDER_MODEL=openai/text-embedding-3-large
224
280
  EMBEDDER_DIMENSIONS=3072
281
+ EMBEDDER_INSTANCE=
282
+ EMBEDDER_API_VERSION=
283
+ EMBEDDER_REGION=
284
+ EMBEDDER_GOOGLE_CREDENTIALS_BASE64=
225
285
 
226
286
  # Logging - Loki (optional)
227
287
  LOKI_ENABLED=false
228
288
  LOKI_HOST=http://localhost:3100
289
+ LOKI_USERNAME=
290
+ LOKI_PASSWORD=
291
+ LOKI_BATCHING=true
292
+ LOKI_INTERVAL=30
293
+ LOKI_APP_LABEL=
229
294
 
230
295
  # Tracing - Tempo (optional)
231
296
  TEMPO_ENABLED=false
232
297
  TEMPO_ENDPOINT=http://localhost:4318/v1/traces
298
+ TEMPO_SERVICE_NAME=my-app
299
+ TEMPO_SERVICE_VERSION=1.0.0
233
300
 
234
301
  # S3 Storage (optional)
235
302
  S3_TYPE=aws
@@ -239,15 +306,23 @@ S3_ACCESS_KEY_ID=
239
306
  S3_SECRET_ACCESS_KEY=
240
307
  S3_REGION=eu-west-1
241
308
 
242
- # Email (optional)
309
+ # Email (supports: sendgrid, smtp, brevo)
243
310
  EMAIL_PROVIDER=sendgrid
244
311
  EMAIL_API_KEY=
245
312
  EMAIL_FROM=noreply@example.com
313
+ EMAIL_HOST=
314
+ EMAIL_PORT=587
315
+ EMAIL_SECURE=false
316
+ EMAIL_USERNAME=
317
+ EMAIL_PASSWORD=
246
318
 
247
319
  # Stripe (optional)
248
320
  STRIPE_SECRET_KEY=
249
321
  STRIPE_PUBLISHABLE_KEY=
250
322
  STRIPE_WEBHOOK_SECRET=
323
+ STRIPE_API_VERSION=2024-12-18.acacia
324
+ STRIPE_PORTAL_RETURN_URL=
325
+ STRIPE_PORTAL_CONFIGURATION_ID=
251
326
 
252
327
  # Push Notifications (optional)
253
328
  VAPID_PUBLIC_KEY=
@@ -258,15 +333,41 @@ VAPID_EMAIL=
258
333
  RATE_LIMIT_ENABLED=true
259
334
  RATE_LIMIT_TTL=60000
260
335
  RATE_LIMIT_REQUESTS=100
336
+ IP_RATE_LIMIT_REQUESTS=20
261
337
 
262
338
  # Encryption
263
339
  ENCRYPTION_KEY=your-32-char-encryption-key
340
+
341
+ # Discord (optional)
342
+ DISCORD_CLIENT_ID=
343
+ DISCORD_CLIENT_SECRET=
344
+ DISCORD_TOKEN=
345
+ DISCORD_DEV_GUILD_ID=
346
+
347
+ # Google (optional)
348
+ GOOGLE_CLIENT_ID=
349
+ GOOGLE_CLIENT_SECRET=
264
350
  ```
265
351
 
266
352
  ## Quick Start
267
353
 
268
354
  The library provides a `bootstrap()` function that handles all the complexity of setting up a NestJS application. You only need to provide your app-specific configuration.
269
355
 
356
+ **What you need:**
357
+ 1. `main.ts` - Bootstrap entry point (~25 lines)
358
+ 2. `config/config.ts` - Optional custom config extending baseConfig
359
+ 3. `features/features.modules.ts` - Your feature modules
360
+
361
+ **What the library handles internally:**
362
+ - AppModule creation (no `app.module.ts` needed)
363
+ - Fastify adapter with multipart support
364
+ - Global validation pipes, exception filters, and interceptors
365
+ - Rate limiting, CORS, and caching
366
+ - i18n internationalization
367
+ - OpenAPI/Swagger documentation
368
+ - Discord bot integration (Worker mode)
369
+ - Graceful shutdown handlers
370
+
270
371
  ### 1. Create Your Features Module
271
372
 
272
373
  ```typescript
@@ -282,7 +383,7 @@ import { Module } from "@nestjs/common";
282
383
  export class FeaturesModules {}
283
384
  ```
284
385
 
285
- ### 3. Create Configuration File (Optional)
386
+ ### 2. Create Configuration File (Optional)
286
387
 
287
388
  If you need custom queues or content types, create a config file:
288
389
 
@@ -304,6 +405,33 @@ export default () => ({
304
405
  });
305
406
  ```
306
407
 
408
+ ### 3. Create OpenAPI Configuration (Optional)
409
+
410
+ ```typescript
411
+ // src/openapi/openapi.config.ts
412
+ import { OpenApiOptions } from "@carlonicora/nestjs-neo4jsonapi";
413
+ import { allEntityDescriptors } from "./entity-registry";
414
+
415
+ export function getOpenApiConfig(): OpenApiOptions {
416
+ const isDevelopment = process.env.NODE_ENV !== "production";
417
+
418
+ return {
419
+ enableSwagger: isDevelopment || process.env.ENABLE_SWAGGER === "true",
420
+ swaggerPath: "/api-docs",
421
+ enableRedoc: isDevelopment || process.env.ENABLE_REDOC === "true",
422
+ redocPath: "/docs",
423
+ entityDescriptors: [...allEntityDescriptors],
424
+ title: "My API",
425
+ description: "RESTful API following JSON:API specification",
426
+ version: process.env.npm_package_version || "1.0.0",
427
+ bearerAuth: true,
428
+ contactEmail: "api@example.com",
429
+ license: "Proprietary",
430
+ licenseUrl: "https://example.com/terms",
431
+ };
432
+ }
433
+ ```
434
+
307
435
  ### 4. Bootstrap Your Application
308
436
 
309
437
  ```typescript
@@ -315,8 +443,9 @@ import * as path from "path";
315
443
  dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
316
444
 
317
445
  import { bootstrap } from "@carlonicora/nestjs-neo4jsonapi";
318
- import config from "./config/config"; // Optional: only if you have custom config
446
+ import config from "./config/config";
319
447
  import { FeaturesModules } from "./features/features.modules";
448
+ import { getOpenApiConfig } from "./openapi/openapi.config";
320
449
 
321
450
  bootstrap({
322
451
  appModules: [FeaturesModules],
@@ -324,26 +453,25 @@ bootstrap({
324
453
  fallbackLanguage: "en",
325
454
  path: path.join(__dirname, "i18n"),
326
455
  },
327
- config, // Optional: pass custom config to extend baseConfig
456
+ config: config,
457
+ contentExtension: {
458
+ additionalRelationships: [],
459
+ },
460
+ openApi: getOpenApiConfig(),
328
461
  });
329
462
  ```
330
463
 
331
- That's it! The `bootstrap()` function handles:
332
-
333
- - Tracing initialization
334
- - API vs Worker mode detection (via `--mode=api` or `--mode=worker` CLI args)
335
- - Fastify adapter with multipart support
336
- - Global validation pipes, exception filters, and interceptors
337
- - Rate limiting, CORS, and caching
338
- - Graceful shutdown handlers
464
+ That's it! The `bootstrap()` function handles everything else.
339
465
 
340
466
  ### Bootstrap Options
341
467
 
342
- | Option | Type | Required | Description |
343
- | ------------ | -------------------------------- | -------- | -------------------------------------------------------------------- |
344
- | `appModules` | `(Type<any> \| DynamicModule)[]` | Yes | Your app-specific feature modules |
345
- | `i18n` | `I18nOptions` | No | i18n configuration (fallbackLanguage, path) |
346
- | `config` | `() => Record<string, any>` | No | Custom config that extends baseConfig (merged with library defaults) |
468
+ | Option | Type | Required | Description |
469
+ | ------------------ | -------------------------------- | -------- | -------------------------------------------------------------------- |
470
+ | `appModules` | `(Type<any> \| DynamicModule)[]` | Yes | Your app-specific feature modules |
471
+ | `i18n` | `I18nOptions` | No | i18n configuration (fallbackLanguage, path) |
472
+ | `config` | `() => Record<string, any>` | No | Custom config that extends baseConfig (merged with library defaults) |
473
+ | `contentExtension` | `ContentExtensionOptions` | No | Additional relationships for Content module |
474
+ | `openApi` | `OpenApiOptions` | No | OpenAPI/Swagger documentation configuration |
347
475
 
348
476
  ### Configuration Options (via `config`)
349
477
 
@@ -356,310 +484,26 @@ The `config` function returns an object that is merged with `baseConfig`. Availa
356
484
  | `jobNames` | `{ process: Record<string, string>, notifications?: Record<string, string> }` | Job names for BullMQ processors (maps content types to job names) |
357
485
  | `prompts.*` | Various | Custom AI agent prompts (see [Customizing Agent Prompts](#customizing-agent-prompts-optional)) |
358
486
 
359
- ---
360
-
361
- ## Advanced Setup (Custom Bootstrap)
362
-
363
- If you need more control over the bootstrap process, you can manually configure the AppModule and main.ts.
364
-
365
- ### 1. Create Configuration File
366
-
367
- ```typescript
368
- // src/config/config.ts
369
- import { baseConfig } from "@carlonicora/nestjs-neo4jsonapi";
370
- import { JobName } from "./enums/job.name";
371
- import { QueueId } from "./enums/queue.id";
372
- // Import your content type metas
373
- import { articleMeta } from "src/features/article/entities/article.meta";
374
- import { documentMeta } from "src/features/document/entities/document.meta";
375
-
376
- export default () => ({
377
- ...baseConfig,
378
- // Register all app queue IDs for background job processing
379
- chunkQueues: {
380
- queueIds: Object.values(QueueId),
381
- },
382
- // Register content type labels for multi-label Neo4j queries
383
- contentTypes: {
384
- types: [
385
- articleMeta.labelName,
386
- documentMeta.labelName,
387
- // Add your content type labels here
388
- ],
389
- },
390
- // Register job names for BullMQ processors
391
- jobNames: JobName,
392
- });
393
- ```
394
-
395
- ```typescript
396
- // src/config/enums/queue.id.ts
397
- export enum QueueId {
398
- CHUNK = "chunk",
399
- DOCUMENT = "document",
400
- ARTICLE = "article",
401
- // Add your custom queue IDs here (lowercase of content type labelName)
402
- }
403
- ```
404
-
405
- ```typescript
406
- // src/config/enums/job.name.ts
407
- export const JobName = {
408
- process: {
409
- chunk: "process_chunk",
410
- Document: "process_document",
411
- Article: "process_article",
412
- // Keys match content type labelName (e.g., "Article", "Document")
413
- // Values are the job names used by processors
414
- },
415
- notifications: {
416
- // Optional notification job names
417
- },
418
- } as const;
419
- ```
420
-
421
- ### 2. Setup App Module
422
-
423
- ```typescript
424
- // src/app.module.ts
425
- import { DynamicModule, Module } from "@nestjs/common";
426
- import { ConfigModule, ConfigService } from "@nestjs/config";
427
- import { EventEmitterModule } from "@nestjs/event-emitter";
428
- import { ScheduleModule } from "@nestjs/schedule";
429
- import { ThrottlerModule } from "@nestjs/throttler";
430
- import { ClsModule } from "nestjs-cls";
431
-
432
- import {
433
- AgentsModule,
434
- AppModeConfig,
435
- AppModeModule,
436
- BaseConfigInterface,
437
- ConfigRateLimitInterface,
438
- CoreModule,
439
- FoundationsModule,
440
- } from "@carlonicora/nestjs-neo4jsonapi";
441
-
442
- // App configuration (includes chunkQueues config)
443
- import config from "./config/config";
444
-
445
- // App-specific modules
446
- import { FeaturesModules } from "src/features/features.modules";
447
-
448
- @Module({})
449
- export class AppModule {
450
- static forRoot(modeConfig: AppModeConfig): DynamicModule {
451
- return {
452
- module: AppModule,
453
- imports: [
454
- // Event emitter for internal events
455
- EventEmitterModule.forRoot(),
456
-
457
- // App mode configuration (API vs Worker)
458
- AppModeModule.forRoot(modeConfig),
459
-
460
- // Configuration
461
- ConfigModule.forRoot({
462
- load: [config],
463
- isGlobal: true,
464
- cache: true,
465
- }),
466
-
467
- // Rate limiting
468
- ThrottlerModule.forRootAsync({
469
- imports: [ConfigModule],
470
- inject: [ConfigService],
471
- useFactory: (configService: ConfigService<BaseConfigInterface>) => {
472
- const rateLimitConfig = configService.get<ConfigRateLimitInterface>("rateLimit");
473
- return {
474
- throttlers: [
475
- {
476
- name: "default",
477
- ttl: rateLimitConfig.ttl,
478
- limit: rateLimitConfig.limit,
479
- },
480
- ],
481
- };
482
- },
483
- }),
484
-
485
- // Request-scoped context (CLS) - required for user/company context
486
- ClsModule.forRoot({
487
- global: true,
488
- middleware: { mount: modeConfig.enableControllers },
489
- }),
490
-
491
- // Scheduled jobs (only enabled in worker mode)
492
- ...(modeConfig.enableCronJobs ? [ScheduleModule.forRoot()] : []),
493
-
494
- // ========================================
495
- // LIBRARY MODULES
496
- // ========================================
497
-
498
- // Core infrastructure (Neo4j, Redis, Cache, Security, etc.)
499
-
500
- // Foundation domain modules (User, Company, Auth, etc.)
501
- // Queues are configured via baseConfig.chunkQueues in config.ts
502
- FoundationsModule,
503
-
504
- // AI Agents (GraphRAG, Summariser, Responder, etc.)
505
- // Prompts are configured via baseConfig.prompts
506
- AgentsModule,
507
-
508
- // ========================================
509
- // YOUR APP-SPECIFIC MODULES
510
- // ========================================
511
- FeaturesModules,
512
- ],
513
- global: true,
514
- controllers: [],
515
- };
516
- }
517
- }
518
- ```
519
-
520
- ### 3. Setup main.ts (Bootstrap)
521
-
522
- ```typescript
523
- // src/main.ts
524
- import * as dotenv from "dotenv";
525
- import * as path from "path";
526
-
527
- // Load environment variables FIRST
528
- dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
529
-
530
- // Initialize tracing BEFORE any other imports
531
- import { tracingSetup } from "@carlonicora/nestjs-neo4jsonapi";
532
- tracingSetup.initialize();
533
-
534
- import { ValidationPipe } from "@nestjs/common";
535
- import { ConfigService } from "@nestjs/config";
536
- import { NestFactory, Reflector } from "@nestjs/core";
537
- import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify";
538
- import { EventEmitter } from "stream";
539
-
540
- import {
541
- AppLoggingService,
542
- AppMode,
543
- AppModeConfig,
544
- BaseConfigInterface,
545
- CacheInterceptor,
546
- CacheService,
547
- ConfigApiInterface,
548
- CorsService,
549
- getAppMode,
550
- getAppModeConfig,
551
- HttpExceptionFilter,
552
- LoggingInterceptor,
553
- TracingInterceptor,
554
- } from "@carlonicora/nestjs-neo4jsonapi";
555
-
556
- import { AppModule } from "./app.module";
557
-
558
- async function bootstrapAPI(modeConfig: AppModeConfig): Promise<void> {
559
- const app = await NestFactory.create<NestFastifyApplication>(
560
- AppModule.forRoot(modeConfig),
561
- new FastifyAdapter({
562
- routerOptions: { ignoreTrailingSlash: true },
563
- bodyLimit: 100 * 1024 * 1024,
564
- }),
565
- { logger: ["error", "warn"] },
566
- );
567
-
568
- const configService = app.get(ConfigService<BaseConfigInterface>);
569
-
570
- // Register multipart support for file uploads
571
- await app.register(require("@fastify/multipart"), {
572
- limits: {
573
- fileSize: 100 * 1024 * 1024,
574
- fieldSize: 10 * 1024 * 1024,
575
- files: 10,
576
- fields: 20,
577
- },
578
- attachFieldsToBody: false,
579
- });
580
-
581
- // Setup logging
582
- const loggingService = app.get(AppLoggingService);
583
- app.useLogger(loggingService);
584
-
585
- // Add Fastify onSend hook for request logging
586
- app
587
- .getHttpAdapter()
588
- .getInstance()
589
- .addHook("onSend", async (request, reply, payload) => {
590
- const startTime = request.raw["requestStartTime"];
591
- if (startTime) {
592
- const responseTime = Date.now() - startTime;
593
- loggingService.logHttpRequest(request.method, request.url, reply.statusCode, responseTime, request.ip);
594
- loggingService.clearRequestContext();
595
- }
596
- return payload;
597
- });
598
-
599
- // Global filters and pipes
600
- app.useGlobalFilters(new HttpExceptionFilter(loggingService));
601
- app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
602
-
603
- // Apply interceptors in order: Tracing -> Cache -> Logging
604
- app.useGlobalInterceptors(app.get(TracingInterceptor));
605
- app.useGlobalInterceptors(new CacheInterceptor(app.get(CacheService), app.get(Reflector), loggingService));
606
- app.useGlobalInterceptors(app.get(LoggingInterceptor));
607
-
608
- // Setup CORS
609
- const corsService = app.get(CorsService);
610
- corsService.validateConfiguration();
611
- app.enableCors(corsService.getCorsConfiguration());
612
-
613
- // Start server
614
- const port = configService.get<ConfigApiInterface>("api").port;
615
- await app.listen(port, "0.0.0.0");
616
- loggingService.log(`API server started on port ${port}`);
617
-
618
- // Graceful shutdown
619
- process.on("SIGTERM", async () => {
620
- await app.close();
621
- process.exit(0);
622
- });
623
- process.on("SIGINT", async () => {
624
- await app.close();
625
- process.exit(0);
626
- });
627
- }
628
-
629
- async function bootstrapWorker(modeConfig: AppModeConfig): Promise<void> {
630
- const app = await NestFactory.createApplicationContext(AppModule.forRoot(modeConfig), {
631
- logger: ["error", "warn"],
632
- });
633
-
634
- const loggingService = app.get(AppLoggingService);
635
- app.useLogger(loggingService);
636
- loggingService.log("Worker process started");
487
+ ### What bootstrap() Handles Internally
637
488
 
638
- process.on("SIGTERM", async () => {
639
- await app.close();
640
- process.exit(0);
641
- });
642
- process.on("SIGINT", async () => {
643
- await app.close();
644
- process.exit(0);
645
- });
646
- }
647
-
648
- async function bootstrap(): Promise<void> {
649
- EventEmitter.defaultMaxListeners = 50;
489
+ The `bootstrap()` function creates a dynamic `AppModule` that includes:
650
490
 
651
- const mode = getAppMode();
652
- const modeConfig = getAppModeConfig(mode);
491
+ - EventEmitterModule for async events
492
+ - AppModeModule (API vs Worker mode)
493
+ - ConfigModule with merged configuration
494
+ - ThrottlerModule for rate limiting
495
+ - ClsModule for request context
496
+ - I18nModule for internationalization
497
+ - ScheduleModule for cron jobs (Worker mode only)
498
+ - CoreModule (all infrastructure modules)
499
+ - FoundationsModule (all domain modules)
500
+ - AgentsModule (AI agents)
501
+ - OpenApiModule for documentation
502
+ - NecordModule + DiscordModule (Worker mode, when DISCORD_TOKEN is set)
653
503
 
654
- if (mode === AppMode.WORKER) {
655
- await bootstrapWorker(modeConfig);
656
- } else {
657
- await bootstrapAPI(modeConfig);
658
- }
659
- }
504
+ > **Note:** For advanced customization scenarios, you can examine the bootstrap source code in `packages/nestjs-neo4jsonapi/src/bootstrap/`. However, the default `bootstrap()` function handles all common use cases.
660
505
 
661
- bootstrap();
662
- ```
506
+ ---
663
507
 
664
508
  ## Company-User Model (B2B & B2C)
665
509
 
@@ -839,17 +683,20 @@ query.query = `
839
683
  your-app/
840
684
  ├── src/
841
685
  │ ├── config/
842
- │ │ ├── config.ts # App configuration
843
- │ │ ├── company.configurations.ts # Company context loader
686
+ │ │ ├── config.ts # App configuration (optional)
844
687
  │ │ └── enums/
845
- │ │ └── queue.id.ts # Queue identifiers
688
+ │ │ └── queue.id.ts # Queue identifiers (if using jobs)
846
689
  │ ├── features/ # Your app-specific modules
847
- ├── app.module.ts
848
- └── main.ts
690
+ │ └── features.modules.ts # Imports all your feature modules
691
+ ├── openapi/ # OpenAPI config (optional)
692
+ │ │ └── openapi.config.ts
693
+ │ └── main.ts # Bootstrap entry (~25 lines)
849
694
  ├── .env
850
695
  └── package.json
851
696
  ```
852
697
 
698
+ **Note:** No `app.module.ts` is required - the library creates this dynamically via `createAppModule()`.
699
+
853
700
  ### Queue IDs and Job Names (if using background jobs)
854
701
 
855
702
  Queue IDs must match the lowercase version of your content type `labelName`:
@@ -882,7 +729,7 @@ export const JobName = {
882
729
 
883
730
  ## Core Modules
884
731
 
885
- The library includes 18 core infrastructure modules:
732
+ The library includes 19 core infrastructure modules:
886
733
 
887
734
  | Module | Description |
888
735
  | ----------------- | ------------------------------------------------ |
@@ -893,7 +740,7 @@ The library includes 18 core infrastructure modules:
893
740
  | `JsonApiModule` | JSON:API specification compliance |
894
741
  | `LoggingModule` | Structured logging with Loki |
895
742
  | `TracingModule` | Distributed tracing with OpenTelemetry |
896
- | `EmailModule` | Email service (SendGrid, SMTP) |
743
+ | `EmailModule` | Email service (SendGrid, SMTP, Brevo) |
897
744
  | `QueueModule` | BullMQ job queue processing |
898
745
  | `WebsocketModule` | Real-time WebSocket communication |
899
746
  | `CorsModule` | CORS configuration |
@@ -1021,35 +868,50 @@ The module includes four health indicators:
1021
868
  | `Neo4jHealthIndicator` | 3s | Executes `RETURN 1` query |
1022
869
  | `RedisHealthIndicator` | 3s | Connection status + PING |
1023
870
  | `S3HealthIndicator` | 5s | Bucket access (HeadBucket) |
1024
- | `DiskHealthIndicator` | - | Free space 1GB or 10% |
871
+ | `DiskHealthIndicator` | - | Free space >= 1GB or 10% |
1025
872
 
1026
873
  ## Foundation Modules
1027
874
 
1028
- The library includes 17 foundation modules for business domain logic:
1029
-
1030
- | Module | Description |
1031
- | -------------------- | ------------------------------------------------ |
1032
- | `UserModule` | User management with CRUD operations |
1033
- | `CompanyModule` | Multi-tenant company management |
1034
- | `AuthModule` | Authentication (login, register, password reset) |
1035
- | `RoleModule` | Role management |
1036
- | `ChunkModule` | Document chunk storage and retrieval |
1037
- | `ChunkerModule` | Document parsing (PDF, DOCX, XLSX, HTML) |
1038
- | `AtomicFactModule` | Atomic facts management for knowledge graphs |
1039
- | `KeyConceptModule` | Key concepts for knowledge graphs |
1040
- | `ContentModule` | Content management |
1041
- | `NotificationModule` | User notifications |
1042
- | `PushModule` | Push notifications (VAPID) |
1043
- | `FeatureModule` | Feature flag management |
1044
- | `ModuleModule` | Module/plugin management |
1045
- | `S3Module` | S3-compatible storage |
1046
- | `TokenUsageModule` | AI token usage tracking |
1047
- | `AuditModule` | Audit logging |
1048
- | `RelevancyModule` | Relevancy scoring |
875
+ The library includes 31 foundation modules for business domain logic:
876
+
877
+ | Module | Description |
878
+ | -------------------------- | -------------------------------------------------- |
879
+ | `UserModule` | User management with CRUD operations |
880
+ | `CompanyModule` | Multi-tenant company management with deletion |
881
+ | `AuthModule` | Authentication (login, register, password reset) |
882
+ | `RoleModule` | Role management |
883
+ | `OAuthModule` | OAuth 2.0 Authorization Server (RFC 6749/7636) |
884
+ | `ChunkModule` | Document chunk storage and retrieval |
885
+ | `ChunkerModule` | Document parsing (PDF, DOCX, XLSX, HTML, PPTX) |
886
+ | `AtomicFactModule` | Atomic facts management for knowledge graphs |
887
+ | `KeyConceptModule` | Key concepts for knowledge graphs |
888
+ | `CommunityModule` | Knowledge graph community storage |
889
+ | `ContentModule` | Content management with extension support |
890
+ | `NotificationModule` | User notifications |
891
+ | `PushModule` | Push notifications (VAPID) |
892
+ | `FeatureModule` | Feature flag management |
893
+ | `ModuleModule` | Module/plugin management |
894
+ | `S3Module` | S3-compatible storage |
895
+ | `TokenUsageModule` | AI token usage tracking |
896
+ | `AuditModule` | Audit logging |
897
+ | `RelevancyModule` | Relevancy scoring |
898
+ | `DiscordModule` | Discord bot integration |
899
+ | `DiscordUserModule` | Discord user management |
900
+ | `GoogleUserModule` | Google OAuth user management |
901
+ | **Stripe Sub-modules:** | |
902
+ | `StripeModule` | Core Stripe integration |
903
+ | `StripeCustomerModule` | Stripe customer management |
904
+ | `StripeSubscriptionModule` | Subscription lifecycle management |
905
+ | `StripeProductModule` | Product management |
906
+ | `StripePriceModule` | Price/plan management with feature selection |
907
+ | `StripeInvoiceModule` | Invoice management |
908
+ | `StripeUsageModule` | Usage-based billing |
909
+ | `StripeWebhookModule` | Webhook processing via BullMQ |
910
+ | `StripeTrialModule` | Trial period management with email notifications |
1049
911
 
1050
912
  ## AI Agents
1051
913
 
1052
- LangChain-powered agents for intelligent document processing.
914
+ The library includes 7 LangChain-powered agent modules for intelligent document processing:
1053
915
 
1054
916
  ### GraphCreatorModule
1055
917
 
@@ -1127,13 +989,275 @@ export class MyService {
1127
989
  }
1128
990
  ```
1129
991
 
992
+ ### CommunityDetectorModule
993
+
994
+ Detects and creates communities from knowledge graph structure.
995
+
996
+ ```typescript
997
+ import { CommunityDetectorService } from "@carlonicora/nestjs-neo4jsonapi";
998
+
999
+ @Injectable()
1000
+ export class MyService {
1001
+ constructor(private readonly communityDetector: CommunityDetectorService) {}
1002
+
1003
+ async detectCommunities() {
1004
+ return this.communityDetector.detect();
1005
+ }
1006
+ }
1007
+ ```
1008
+
1009
+ ### CommunitySummariserModule
1010
+
1011
+ Generates summaries for detected communities.
1012
+
1013
+ ```typescript
1014
+ import { CommunitySummariserService } from "@carlonicora/nestjs-neo4jsonapi";
1015
+
1016
+ @Injectable()
1017
+ export class MyService {
1018
+ constructor(private readonly summariser: CommunitySummariserService) {}
1019
+
1020
+ async summarizeCommunity(communityId: string) {
1021
+ return this.summariser.summarize({ communityId });
1022
+ }
1023
+ }
1024
+ ```
1025
+
1026
+ ### DriftModule
1027
+
1028
+ See [DRIFT Module (Advanced Semantic Search)](#drift-module-advanced-semantic-search) for detailed documentation.
1029
+
1030
+ ## DRIFT Module (Advanced Semantic Search)
1031
+
1032
+ DRIFT (Dynamic Retrieval with Intelligence and Future-aware Thinking) is an advanced semantic search system that uses community detection and HyDE (Hypothetical Document Embedding) for sophisticated query understanding.
1033
+
1034
+ ### Architecture
1035
+
1036
+ DRIFT uses a multi-node workflow:
1037
+
1038
+ | Node | Purpose |
1039
+ | ---------------------------- | --------------------------------------------- |
1040
+ | `HydeNodeService` | Generates hypothetical document embedding |
1041
+ | `CommunitySearchNodeService` | Vector search against community summaries |
1042
+ | `PrimerAnswerNodeService` | Generates initial answer + follow-up questions|
1043
+ | `FollowUpNodeService` | Processes follow-up questions iteratively |
1044
+ | `SynthesisNodeService` | Combines all answers into final response |
1045
+
1046
+ ### Usage
1047
+
1048
+ ```typescript
1049
+ import { DriftSearchService } from "@carlonicora/nestjs-neo4jsonapi";
1050
+
1051
+ @Injectable()
1052
+ export class MyService {
1053
+ constructor(private readonly driftSearch: DriftSearchService) {}
1054
+
1055
+ async searchWithDrift(question: string) {
1056
+ // Full DRIFT workflow: HyDE -> Community Search -> Primer Answer -> Follow-ups -> Synthesis
1057
+ const result = await this.driftSearch.search({
1058
+ question,
1059
+ config: {
1060
+ primerTopK: 5, // Top K communities to search
1061
+ followUpDepth: 2, // Depth of follow-up question iterations
1062
+ },
1063
+ });
1064
+
1065
+ // result contains:
1066
+ // - answer: Final synthesized answer
1067
+ // - matchedCommunities: Communities that matched the query
1068
+ // - followUpAnswers: Answers from follow-up questions
1069
+ // - initialAnswer: Initial answer before follow-ups
1070
+ // - confidence: Confidence score
1071
+ // - hydeEmbedding: Generated hypothetical document embedding
1072
+ return result;
1073
+ }
1074
+
1075
+ async quickSearch(question: string) {
1076
+ // Quick search without follow-ups (HyDE + Community Search + Primer only)
1077
+ return this.driftSearch.quickSearch({ question, topK: 5 });
1078
+ }
1079
+ }
1080
+ ```
1081
+
1082
+ ### How DRIFT Works
1083
+
1084
+ 1. **HyDE Generation**: Creates a hypothetical document that would answer the question
1085
+ 2. **Community Search**: Uses the HyDE embedding to find relevant communities
1086
+ 3. **Primer Answer**: Generates an initial answer and identifies follow-up questions
1087
+ 4. **Follow-up Processing**: Recursively explores follow-up questions for deeper context
1088
+ 5. **Synthesis**: Combines all gathered information into a comprehensive final answer
1089
+
1090
+ ## OpenAPI Documentation
1091
+
1092
+ The library includes comprehensive OpenAPI/Swagger documentation support with JSON:API compliance.
1093
+
1094
+ ### Configuration
1095
+
1096
+ Enable OpenAPI in your bootstrap options:
1097
+
1098
+ ```typescript
1099
+ import { bootstrap } from "@carlonicora/nestjs-neo4jsonapi";
1100
+
1101
+ bootstrap({
1102
+ appModules: [FeaturesModules],
1103
+ openApi: {
1104
+ enableSwagger: true,
1105
+ swaggerPath: '/api-docs',
1106
+ enableRedoc: true,
1107
+ redocPath: '/docs',
1108
+ title: 'My API',
1109
+ description: 'RESTful API following JSON:API specification',
1110
+ version: '1.0.0',
1111
+ bearerAuth: true,
1112
+ contactEmail: 'api@example.com',
1113
+ license: 'Proprietary',
1114
+ licenseUrl: 'https://example.com/terms',
1115
+ entityDescriptors: [
1116
+ PhotographDescriptor,
1117
+ RollDescriptor,
1118
+ // ... your entity descriptors
1119
+ ],
1120
+ },
1121
+ });
1122
+ ```
1123
+
1124
+ ### OpenAPI Options
1125
+
1126
+ | Option | Type | Default | Description |
1127
+ | ------------------- | ------- | ------------ | -------------------------------------- |
1128
+ | `enableSwagger` | boolean | false | Enable Swagger UI endpoint |
1129
+ | `swaggerPath` | string | '/api-docs' | Path for Swagger UI |
1130
+ | `enableRedoc` | boolean | false | Enable Redoc endpoint |
1131
+ | `redocPath` | string | '/docs' | Path for Redoc |
1132
+ | `title` | string | - | API documentation title |
1133
+ | `description` | string | - | API description (supports markdown) |
1134
+ | `version` | string | - | API version |
1135
+ | `bearerAuth` | boolean | true | Enable JWT Bearer auth in docs |
1136
+ | `contactEmail` | string | - | Contact email |
1137
+ | `license` | string | - | License name |
1138
+ | `licenseUrl` | string | - | License URL |
1139
+ | `entityDescriptors` | array | [] | Entity descriptors for schema generation |
1140
+
1141
+ ### JSON:API Decorators
1142
+
1143
+ ```typescript
1144
+ import {
1145
+ ApiJsonApiResponse,
1146
+ ApiJsonApiListQuery,
1147
+ ApiJsonApiListErrors,
1148
+ ApiJsonApiCreateErrors,
1149
+ ApiJsonApiDeleteErrors,
1150
+ } from "@carlonicora/nestjs-neo4jsonapi";
1151
+
1152
+ @Controller('photographs')
1153
+ export class PhotographController {
1154
+ @Get(':id')
1155
+ @ApiJsonApiResponse(PhotographDescriptor)
1156
+ async findById() { ... }
1157
+
1158
+ @Get()
1159
+ @ApiJsonApiResponse(PhotographDescriptor, { isList: true })
1160
+ @ApiJsonApiListQuery()
1161
+ @ApiJsonApiListErrors()
1162
+ async findAll() { ... }
1163
+
1164
+ @Post()
1165
+ @ApiJsonApiResponse(PhotographDescriptor, { status: 201 })
1166
+ @ApiJsonApiCreateErrors()
1167
+ async create() { ... }
1168
+
1169
+ @Delete(':id')
1170
+ @HttpCode(HttpStatus.NO_CONTENT)
1171
+ @ApiJsonApiDeleteErrors()
1172
+ async delete() { ... }
1173
+ }
1174
+ ```
1175
+
1176
+ ## OAuth 2.0 Support
1177
+
1178
+ The library implements a complete OAuth 2.0 Authorization Server following RFC 6749 and RFC 7636 (PKCE).
1179
+
1180
+ ### Enabling OAuth
1181
+
1182
+ ```env
1183
+ OAUTH_ENABLED=true
1184
+ OAUTH_AUTHORIZATION_CODE_LIFETIME=600
1185
+ OAUTH_ACCESS_TOKEN_LIFETIME=3600
1186
+ OAUTH_REFRESH_TOKEN_LIFETIME=604800
1187
+ OAUTH_REQUIRE_PKCE_FOR_PUBLIC_CLIENTS=true
1188
+ OAUTH_ROTATE_REFRESH_TOKENS=true
1189
+ ```
1190
+
1191
+ ### OAuth Endpoints
1192
+
1193
+ | Endpoint | Method | Purpose |
1194
+ | ------------------- | ------ | ------------------------------ |
1195
+ | `/oauth/authorize` | GET | Authorization endpoint |
1196
+ | `/oauth/token` | POST | Token endpoint |
1197
+ | `/oauth/revoke` | POST | Token revocation (RFC 7009) |
1198
+ | `/oauth/introspect` | POST | Token introspection (RFC 7662) |
1199
+ | `/oauth/clients` | CRUD | Client management |
1200
+
1201
+ ### Protecting Endpoints with OAuth
1202
+
1203
+ ```typescript
1204
+ import {
1205
+ JwtOrOAuthGuard,
1206
+ OAuthScopes,
1207
+ } from "@carlonicora/nestjs-neo4jsonapi";
1208
+
1209
+ @Controller('photographs')
1210
+ export class PhotographController {
1211
+ // Accepts both JWT and OAuth tokens (migration path)
1212
+ @Get(':id')
1213
+ @UseGuards(JwtOrOAuthGuard)
1214
+ @OAuthScopes('photographs:read')
1215
+ async findById() { ... }
1216
+
1217
+ @Post()
1218
+ @UseGuards(JwtOrOAuthGuard)
1219
+ @OAuthScopes('photographs:read', 'photographs:write')
1220
+ async create() { ... }
1221
+ }
1222
+ ```
1223
+
1224
+ ### Context Set by OAuth
1225
+
1226
+ When using OAuth guards, the following context is set in CLS:
1227
+
1228
+ ```typescript
1229
+ // In your service
1230
+ const userId = this.cls.get('userId');
1231
+ const companyId = this.cls.get('companyId');
1232
+ const clientId = this.cls.get('oauthClientId');
1233
+ const scopes = this.cls.get('oauthScopes');
1234
+ const authType = this.cls.get('authType'); // 'oauth' or 'jwt'
1235
+ ```
1236
+
1130
1237
  ## Security & Authentication
1131
1238
 
1239
+ ### Available Guards
1240
+
1241
+ | Guard | Purpose |
1242
+ | -------------------- | -------------------------------------------- |
1243
+ | `JwtAuthGuard` | Requires valid JWT token |
1244
+ | `AdminJwtAuthGuard` | Requires Administrator role |
1245
+ | `OptionalJwtAuthGuard` | JWT optional (works for anonymous users) |
1246
+ | `JwtOrOAuthGuard` | Accepts OAuth first, falls back to JWT |
1247
+
1132
1248
  ### Using Guards
1133
1249
 
1134
1250
  ```typescript
1135
1251
  import { Controller, Get, UseGuards } from "@nestjs/common";
1136
- import { JwtAuthGuard, AdminJwtAuthGuard, OptionalJwtAuthGuard, Roles, SystemRoles } from "@carlonicora/nestjs-neo4jsonapi";
1252
+ import {
1253
+ JwtAuthGuard,
1254
+ AdminJwtAuthGuard,
1255
+ OptionalJwtAuthGuard,
1256
+ JwtOrOAuthGuard,
1257
+ Roles,
1258
+ OAuthScopes,
1259
+ SystemRoles
1260
+ } from "@carlonicora/nestjs-neo4jsonapi";
1137
1261
  import { AppRoles } from "./config/roles";
1138
1262
 
1139
1263
  @Controller("api/resources")
@@ -1153,6 +1277,12 @@ export class ResourceController {
1153
1277
  @UseGuards(OptionalJwtAuthGuard)
1154
1278
  async getPublicResources() { ... }
1155
1279
 
1280
+ // Accepts both JWT and OAuth tokens
1281
+ @Get("oauth-or-jwt")
1282
+ @UseGuards(JwtOrOAuthGuard)
1283
+ @OAuthScopes('resources:read')
1284
+ async getResourcesWithOAuth() { ... }
1285
+
1156
1286
  // Requires specific roles (UUIDs)
1157
1287
  @Get("restricted")
1158
1288
  @UseGuards(JwtAuthGuard)
@@ -1184,6 +1314,10 @@ export class MyService {
1184
1314
  const roles = this.cls.get("roles");
1185
1315
  const language = this.cls.get("language");
1186
1316
 
1317
+ // OAuth-specific context (when using OAuth)
1318
+ const authType = this.cls.get("authType"); // 'oauth' or 'jwt'
1319
+ const oauthScopes = this.cls.get("oauthScopes");
1320
+
1187
1321
  if (config?.hasModule("premium-feature")) {
1188
1322
  // User's company has access to premium feature
1189
1323
  }
@@ -1191,25 +1325,228 @@ export class MyService {
1191
1325
  }
1192
1326
  ```
1193
1327
 
1328
+ ## Company Deletion Handler
1329
+
1330
+ The library supports custom company deletion handlers for application-specific cleanup.
1331
+
1332
+ ### Interface
1333
+
1334
+ ```typescript
1335
+ import {
1336
+ CompanyDeletionHandler,
1337
+ COMPANY_DELETION_HANDLER,
1338
+ DeletionReason,
1339
+ DeletionOptions
1340
+ } from "@carlonicora/nestjs-neo4jsonapi";
1341
+
1342
+ type DeletionReason = 'trial_expired' | 'subscription_cancelled' | 'immediate_deletion';
1343
+
1344
+ interface DeletionOptions {
1345
+ sendEmail?: boolean;
1346
+ reason?: DeletionReason;
1347
+ }
1348
+
1349
+ interface CompanyDeletionHandler {
1350
+ deleteCompany(companyId: string, companyName: string, options?: DeletionOptions): Promise<void>;
1351
+ }
1352
+ ```
1353
+
1354
+ ### Implementation
1355
+
1356
+ ```typescript
1357
+ import { Injectable } from "@nestjs/common";
1358
+ import {
1359
+ CompanyDeletionHandler,
1360
+ DeletionOptions,
1361
+ S3Service,
1362
+ } from "@carlonicora/nestjs-neo4jsonapi";
1363
+
1364
+ @Injectable()
1365
+ export class CompanyDeletionService implements CompanyDeletionHandler {
1366
+ constructor(
1367
+ private readonly s3: S3Service,
1368
+ private readonly auditLogger: AuditLogger,
1369
+ ) {}
1370
+
1371
+ async deleteCompany(companyId: string, companyName: string, options?: DeletionOptions) {
1372
+ // 1. Delete S3 objects
1373
+ await this.s3.deleteCompanyObjects(companyId);
1374
+
1375
+ // 2. Log audit event
1376
+ await this.auditLogger.log({
1377
+ action: 'company_deleted',
1378
+ companyId,
1379
+ companyName,
1380
+ reason: options?.reason,
1381
+ });
1382
+
1383
+ // 3. Send notification email if requested
1384
+ if (options?.sendEmail) {
1385
+ await this.sendDeletionEmail(companyName, options.reason);
1386
+ }
1387
+ }
1388
+ }
1389
+ ```
1390
+
1391
+ ### Registration
1392
+
1393
+ ```typescript
1394
+ import { Module, Global } from "@nestjs/common";
1395
+ import { COMPANY_DELETION_HANDLER } from "@carlonicora/nestjs-neo4jsonapi";
1396
+
1397
+ @Global()
1398
+ @Module({
1399
+ providers: [
1400
+ CompanyDeletionService,
1401
+ {
1402
+ provide: COMPANY_DELETION_HANDLER,
1403
+ useExisting: CompanyDeletionService,
1404
+ },
1405
+ ],
1406
+ exports: [CompanyDeletionService, COMPANY_DELETION_HANDLER],
1407
+ })
1408
+ export class CompanyDeletionModule {}
1409
+ ```
1410
+
1411
+ ## Entity Descriptors (defineEntity)
1412
+
1413
+ Entity Descriptors provide a single source of truth for entity configuration, auto-generating mappers, serializers, constraints, and indexes.
1414
+
1415
+ ### Basic Usage
1416
+
1417
+ ```typescript
1418
+ import { defineEntity, Entity, S3Service } from "@carlonicora/nestjs-neo4jsonapi";
1419
+ import { rollMeta, companyMeta } from "@carlonicora/nestjs-neo4jsonapi";
1420
+
1421
+ // 1. Define entity type
1422
+ export type Photograph = Entity & {
1423
+ url: string;
1424
+ filename?: string;
1425
+ stars: number;
1426
+ roll: Roll;
1427
+ company: Company;
1428
+ };
1429
+
1430
+ // 2. Create entity descriptor
1431
+ export const PhotographDescriptor = defineEntity<Photograph>()({
1432
+ // Meta (replaces .meta.ts file)
1433
+ type: "photographs",
1434
+ endpoint: "photographs",
1435
+ nodeName: "photograph",
1436
+ labelName: "Photograph",
1437
+
1438
+ // Inject services for field transformers
1439
+ injectServices: [S3Service],
1440
+
1441
+ // Field definitions
1442
+ fields: {
1443
+ url: {
1444
+ type: "string",
1445
+ required: true,
1446
+ transform: async (data, services) => {
1447
+ return await services.S3Service.generateSignedUrl({ key: data.url });
1448
+ },
1449
+ },
1450
+ filename: { type: "string" },
1451
+ stars: { type: "number", default: 0 },
1452
+ },
1453
+
1454
+ // Computed fields (from Neo4j record)
1455
+ computed: {
1456
+ position: {
1457
+ compute: (params) => params.record.get("position"),
1458
+ meta: true, // Goes to JSON:API meta
1459
+ },
1460
+ },
1461
+
1462
+ // Relationships
1463
+ relationships: {
1464
+ roll: {
1465
+ model: rollMeta,
1466
+ direction: "out",
1467
+ relationship: "FRAME_OF",
1468
+ cardinality: "one",
1469
+ dtoKey: "roll",
1470
+ fields: [{ name: "position", type: "number", required: true }],
1471
+ },
1472
+ company: {
1473
+ model: companyMeta,
1474
+ direction: "out",
1475
+ relationship: "BELONGS_TO",
1476
+ cardinality: "one",
1477
+ },
1478
+ },
1479
+ });
1480
+ ```
1481
+
1482
+ ### Field Types
1483
+
1484
+ | Type | Neo4j/Cypher | JSON |
1485
+ | ---------- | --------------- | -------- |
1486
+ | `string` | STRING | string |
1487
+ | `number` | INTEGER/FLOAT | number |
1488
+ | `boolean` | BOOLEAN | boolean |
1489
+ | `date` | DATE | string (ISO) |
1490
+ | `datetime` | DATETIME | string (ISO) |
1491
+ | `json` | MAP | object |
1492
+ | `string[]` | LIST\<STRING\> | string[] |
1493
+ | `number[]` | LIST\<INTEGER\> | number[] |
1494
+
1495
+ ### Field Options
1496
+
1497
+ | Option | Type | Description |
1498
+ | ----------- | -------- | ---------------------------------- |
1499
+ | `type` | CypherType | Data type |
1500
+ | `required` | boolean | Required field |
1501
+ | `default` | any | Default value |
1502
+ | `meta` | boolean | Put in JSON:API meta |
1503
+ | `transform` | function | Async transform for serialization |
1504
+
1505
+ ### Relationship Options
1506
+
1507
+ | Option | Type | Description |
1508
+ | ------------- | ----------------- | --------------------------------- |
1509
+ | `model` | DataMeta | Related entity metadata |
1510
+ | `direction` | 'in' \| 'out' | Relationship direction |
1511
+ | `relationship`| string | Neo4j relationship type |
1512
+ | `cardinality` | 'one' \| 'many' | Single or collection |
1513
+ | `required` | boolean | Use MATCH vs OPTIONAL MATCH |
1514
+ | `contextKey` | string | CLS context key for value |
1515
+ | `dtoKey` | string | Override key in DTO |
1516
+ | `fields` | array | Edge properties |
1517
+
1518
+ ### What defineEntity Auto-Generates
1519
+
1520
+ - **Constraints**: Unique constraint on `id`
1521
+ - **Indexes**: FULLTEXT index on all string fields
1522
+ - **Mapper**: Entity-to-record mapping
1523
+ - **Serializer**: JSON:API compliant serialization
1524
+
1194
1525
  ## Customizing Agent Prompts (Optional)
1195
1526
 
1196
1527
  The library includes default prompts. Customization is entirely optional.
1197
1528
 
1198
1529
  ### Available Prompts
1199
1530
 
1200
- | Agent | Config Key | Purpose |
1201
- | ------------------ | --------------------------------------------- | ------------------------------------- |
1202
- | **GraphCreator** | `prompts.graphCreator` | Extract atomic facts and key concepts |
1203
- | **Contextualiser** | `prompts.contextualiser.questionRefiner` | Refine user questions |
1204
- | **Contextualiser** | `prompts.contextualiser.rationalPlan` | Create rational plans |
1205
- | **Contextualiser** | `prompts.contextualiser.keyConceptExtractor` | Score key concepts |
1206
- | **Contextualiser** | `prompts.contextualiser.atomicFactsExtractor` | Evaluate atomic facts |
1207
- | **Contextualiser** | `prompts.contextualiser.chunk` | Assess text chunks |
1208
- | **Contextualiser** | `prompts.contextualiser.chunkVector` | Vector-based chunk retrieval |
1209
- | **Responder** | `prompts.responder` | Generate final answers |
1210
- | **Summariser** | `prompts.summariser.map` | Summarize individual chunks |
1211
- | **Summariser** | `prompts.summariser.combine` | Combine summaries |
1212
- | **Summariser** | `prompts.summariser.tldr` | Create TLDR |
1531
+ | Agent | Config Key | Purpose |
1532
+ | ---------------------- | --------------------------------------------- | ------------------------------------- |
1533
+ | **GraphCreator** | `prompts.graphCreator` | Extract atomic facts and key concepts |
1534
+ | **Contextualiser** | `prompts.contextualiser.questionRefiner` | Refine user questions |
1535
+ | **Contextualiser** | `prompts.contextualiser.rationalPlan` | Create rational plans |
1536
+ | **Contextualiser** | `prompts.contextualiser.keyConceptExtractor` | Score key concepts |
1537
+ | **Contextualiser** | `prompts.contextualiser.atomicFactsExtractor` | Evaluate atomic facts |
1538
+ | **Contextualiser** | `prompts.contextualiser.chunk` | Assess text chunks |
1539
+ | **Contextualiser** | `prompts.contextualiser.chunkVector` | Vector-based chunk retrieval |
1540
+ | **Responder** | `prompts.responder` | Generate final answers |
1541
+ | **Summariser** | `prompts.summariser.map` | Summarize individual chunks |
1542
+ | **Summariser** | `prompts.summariser.combine` | Combine summaries |
1543
+ | **Summariser** | `prompts.summariser.tldr` | Create TLDR |
1544
+ | **CommunityDetector** | `prompts.communityDetector` | Community detection |
1545
+ | **CommunitySummariser**| `prompts.communitySummariser` | Community summarization |
1546
+ | **DRIFT** | `prompts.drift.hyde` | HyDE generation |
1547
+ | **DRIFT** | `prompts.drift.primerAnswer` | Initial answer generation |
1548
+ | **DRIFT** | `prompts.drift.followUp` | Follow-up question handling |
1549
+ | **DRIFT** | `prompts.drift.synthesis` | Final answer synthesis |
1213
1550
 
1214
1551
  ### Custom Prompts Example
1215
1552