@vipin733/nodescope 0.1.0 → 0.2.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
@@ -9,7 +9,7 @@
9
9
 
10
10
  - 📊 **Real-time Monitoring** - Watch requests, queries, and logs as they happen via WebSocket streaming
11
11
  - 💾 **Multiple Storage Backends** - Choose from in-memory, SQLite, PostgreSQL, or MySQL
12
- - 🔌 **Framework Agnostic** - First-class support for Express, Hono, Fastify, and vanilla Node.js
12
+ - 🔌 **Framework Agnostic** - First-class support for Express, Hono, Fastify, NestJS, and vanilla Node.js
13
13
  - 🎨 **Beautiful Dashboard** - React-based UI with dark/light mode (coming soon)
14
14
  - 🚀 **Zero Configuration** - Sensible defaults, just install and go
15
15
  - 🔍 **Comprehensive Watchers** - Track requests, database queries, logs, exceptions, and more
@@ -119,6 +119,96 @@ fastify.get('/', async (request, reply) => {
119
119
  await fastify.listen({ port: 3000 });
120
120
  ```
121
121
 
122
+ ### NestJS
123
+
124
+ ```typescript
125
+ import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
126
+ import { NestFactory } from '@nestjs/core';
127
+ import {
128
+ NodeScope,
129
+ NodeScopeModule,
130
+ NodeScopeMiddleware,
131
+ setupNodeScopeRoutes
132
+ } from '@vipin733/nodescope';
133
+
134
+ @Module({
135
+ imports: [
136
+ NodeScopeModule.forRoot({
137
+ storage: 'memory',
138
+ dashboardPath: '/_debug',
139
+ }),
140
+ ],
141
+ })
142
+ export class AppModule implements NestModule {
143
+ constructor(private readonly nodescope: NodeScope) {}
144
+
145
+ configure(consumer: MiddlewareConsumer) {
146
+ // Apply NodeScope middleware to all routes
147
+ const middleware = new NodeScopeMiddleware(this.nodescope);
148
+ consumer
149
+ .apply((req, res, next) => middleware.use(req, res, next))
150
+ .forRoutes('*');
151
+ }
152
+ }
153
+
154
+ async function bootstrap() {
155
+ const app = await NestFactory.create(AppModule);
156
+
157
+ // Set up dashboard routes
158
+ const nodescope = app.get('NODESCOPE_INSTANCE');
159
+ await setupNodeScopeRoutes(app, nodescope);
160
+
161
+ await app.listen(3000);
162
+ }
163
+ bootstrap();
164
+ ```
165
+
166
+ #### NestJS with ConfigService (Async Configuration)
167
+
168
+ ```typescript
169
+ import { Module } from '@nestjs/common';
170
+ import { ConfigModule, ConfigService } from '@nestjs/config';
171
+ import { NodeScopeModule } from '@vipin733/nodescope';
172
+
173
+ @Module({
174
+ imports: [
175
+ ConfigModule.forRoot(),
176
+ NodeScopeModule.forRootAsync({
177
+ useFactory: (configService: ConfigService) => ({
178
+ storage: configService.get('NODESCOPE_STORAGE') || 'sqlite',
179
+ dashboardPath: '/_debug',
180
+ enabled: configService.get('NODE_ENV') === 'development',
181
+ }),
182
+ inject: [ConfigService],
183
+ }),
184
+ ],
185
+ })
186
+ export class AppModule {}
187
+ ```
188
+
189
+ #### NestJS Global Interceptor (Alternative to Middleware)
190
+
191
+ ```typescript
192
+ import { Module } from '@nestjs/common';
193
+ import { APP_INTERCEPTOR } from '@nestjs/core';
194
+ import { NodeScopeModule, NodeScopeInterceptor } from '@vipin733/nodescope';
195
+
196
+ @Module({
197
+ imports: [
198
+ NodeScopeModule.forRoot({
199
+ storage: 'memory',
200
+ }),
201
+ ],
202
+ providers: [
203
+ {
204
+ provide: APP_INTERCEPTOR,
205
+ useClass: NodeScopeInterceptor,
206
+ },
207
+ ],
208
+ })
209
+ export class AppModule {}
210
+ ```
211
+
122
212
  ## ⚙️ Configuration
123
213
 
124
214
  ### Constructor Options
@@ -367,6 +457,28 @@ import { createFastifyPlugin } from '@vipin733/nodescope';
367
457
  await fastify.register(createFastifyPlugin, options);
368
458
  ```
369
459
 
460
+ #### NestJS
461
+
462
+ ```typescript
463
+ import { NodeScopeModule, NodeScopeMiddleware, NodeScopeInterceptor } from '@vipin733/nodescope';
464
+
465
+ // Module approach
466
+ @Module({
467
+ imports: [NodeScopeModule.forRoot(options)],
468
+ })
469
+
470
+ // Or async configuration
471
+ NodeScopeModule.forRootAsync({
472
+ useFactory: (config: ConfigService) => ({...}),
473
+ inject: [ConfigService],
474
+ })
475
+
476
+ // Use middleware or interceptor
477
+ consumer.apply(NodeScopeMiddleware).forRoutes('*');
478
+ // or
479
+ { provide: APP_INTERCEPTOR, useClass: NodeScopeInterceptor }
480
+ ```
481
+
370
482
  ## 🛠️ Development
371
483
 
372
484
  ```bash
@@ -394,6 +506,7 @@ Check out the [examples](https://github.com/yourusername/nodescope/tree/main/exa
394
506
  - Express.js example
395
507
  - Hono example
396
508
  - Fastify example
509
+ - NestJS example
397
510
 
398
511
  ## 🤝 Contributing
399
512
 
package/dist/index.cjs CHANGED
@@ -806,6 +806,10 @@ __export(index_exports, {
806
806
  MemoryStorage: () => MemoryStorage,
807
807
  MySQLStorage: () => MySQLStorage,
808
808
  NodeScope: () => NodeScope,
809
+ NodeScopeController: () => NodeScopeController,
810
+ NodeScopeInterceptor: () => NodeScopeInterceptor,
811
+ NodeScopeMiddleware: () => NodeScopeMiddleware,
812
+ NodeScopeModule: () => NodeScopeModule,
809
813
  PostgreSQLStorage: () => PostgreSQLStorage,
810
814
  QueryWatcher: () => QueryWatcher,
811
815
  RealTimeServer: () => RealTimeServer,
@@ -831,6 +835,7 @@ __export(index_exports, {
831
835
  mountExpressRoutes: () => mountExpressRoutes,
832
836
  nodescope: () => nodescope,
833
837
  setupGlobalErrorHandlers: () => setupGlobalErrorHandlers,
838
+ setupNodeScopeRoutes: () => setupNodeScopeRoutes,
834
839
  wrapFetch: () => wrapFetch,
835
840
  wrapJobProcessor: () => wrapJobProcessor,
836
841
  wrapPrisma: () => wrapPrisma
@@ -2882,6 +2887,317 @@ async function fastifyNodeScope(fastify, options) {
2882
2887
  return reply.status(response.status).send(response.body);
2883
2888
  });
2884
2889
  }
2890
+
2891
+ // src/adapters/nestjs.ts
2892
+ var NodeScopeMiddleware = class {
2893
+ constructor(nodescope2) {
2894
+ this.nodescope = nodescope2;
2895
+ }
2896
+ async use(req, res, next) {
2897
+ const nodescope2 = this.nodescope;
2898
+ if (!nodescope2 || !nodescope2.isEnabled) {
2899
+ return next();
2900
+ }
2901
+ const dashboardPath = nodescope2.dashboardPath;
2902
+ if (req.path?.startsWith(dashboardPath)) {
2903
+ return next();
2904
+ }
2905
+ const ctx = nodescope2.createContext();
2906
+ req.nodescope = ctx;
2907
+ const originalSend = res.send;
2908
+ const originalJson = res.json;
2909
+ let responseBody;
2910
+ res.send = function(body) {
2911
+ responseBody = body;
2912
+ return originalSend.call(this, body);
2913
+ };
2914
+ res.json = function(body) {
2915
+ responseBody = body;
2916
+ return originalJson.call(this, body);
2917
+ };
2918
+ res.on("finish", async () => {
2919
+ if (!nodescope2.requestWatcher.enabled) return;
2920
+ try {
2921
+ const entry = nodescope2.requestWatcher.record({
2922
+ batchId: ctx.batchId,
2923
+ startTime: ctx.startTime,
2924
+ method: req.method,
2925
+ url: req.originalUrl || req.url,
2926
+ path: req.path || req.route?.path,
2927
+ query: req.query,
2928
+ headers: req.headers,
2929
+ body: req.body,
2930
+ ip: req.ip || req.socket?.remoteAddress,
2931
+ userAgent: req.get?.("user-agent"),
2932
+ session: req.session,
2933
+ response: {
2934
+ status: res.statusCode,
2935
+ headers: res.getHeaders?.() || {},
2936
+ body: responseBody
2937
+ }
2938
+ });
2939
+ if (entry) {
2940
+ await nodescope2.recordEntry(entry);
2941
+ }
2942
+ } catch (error) {
2943
+ }
2944
+ });
2945
+ next();
2946
+ }
2947
+ };
2948
+ var NodeScopeInterceptor = class {
2949
+ constructor(nodescope2) {
2950
+ this.nodescope = nodescope2;
2951
+ }
2952
+ async intercept(context, next) {
2953
+ if (!this.nodescope.isEnabled) {
2954
+ return next.handle();
2955
+ }
2956
+ const http = context.switchToHttp();
2957
+ const request = http.getRequest();
2958
+ const response = http.getResponse();
2959
+ const dashboardPath = this.nodescope.dashboardPath;
2960
+ if (request.path?.startsWith(dashboardPath)) {
2961
+ return next.handle();
2962
+ }
2963
+ const ctx = this.nodescope.createContext();
2964
+ request.nodescope = ctx;
2965
+ const { tap } = await import("rxjs/operators");
2966
+ return next.handle().pipe(
2967
+ tap({
2968
+ next: async (data) => {
2969
+ if (!this.nodescope.requestWatcher.enabled) return;
2970
+ try {
2971
+ const entry = this.nodescope.requestWatcher.record({
2972
+ batchId: ctx.batchId,
2973
+ startTime: ctx.startTime,
2974
+ method: request.method,
2975
+ url: request.originalUrl || request.url,
2976
+ path: request.path || request.route?.path,
2977
+ query: request.query,
2978
+ headers: request.headers,
2979
+ body: request.body,
2980
+ ip: request.ip || request.socket?.remoteAddress,
2981
+ userAgent: request.get?.("user-agent"),
2982
+ session: request.session,
2983
+ response: {
2984
+ status: response.statusCode,
2985
+ headers: response.getHeaders?.() || {},
2986
+ body: data
2987
+ }
2988
+ });
2989
+ if (entry) {
2990
+ await this.nodescope.recordEntry(entry);
2991
+ }
2992
+ } catch (error) {
2993
+ console.error("NodeScope error recording request:", error);
2994
+ }
2995
+ },
2996
+ error: async (error) => {
2997
+ if (!this.nodescope.exceptionWatcher.enabled) return;
2998
+ try {
2999
+ const errorObj = error instanceof Error ? error : new Error(String(error));
3000
+ const entry = this.nodescope.exceptionWatcher.record({
3001
+ error: errorObj,
3002
+ context: {
3003
+ method: request.method,
3004
+ url: request.url,
3005
+ statusCode: error.status || 500
3006
+ }
3007
+ });
3008
+ if (entry) {
3009
+ await this.nodescope.recordEntry(entry);
3010
+ }
3011
+ } catch (recordError) {
3012
+ console.error("NodeScope error recording exception:", recordError);
3013
+ }
3014
+ }
3015
+ })
3016
+ );
3017
+ }
3018
+ };
3019
+ var NodeScopeController = class {
3020
+ constructor(nodescope2) {
3021
+ this.nodescope = nodescope2;
3022
+ }
3023
+ async getDashboard(req, res) {
3024
+ const authorized = await this.nodescope.checkAuthorization(req);
3025
+ if (!authorized) {
3026
+ res.status(403).json({ error: "Unauthorized" });
3027
+ return;
3028
+ }
3029
+ res.setHeader("Content-Type", "text/html");
3030
+ res.send(getDashboardHtml(this.nodescope.dashboardPath));
3031
+ }
3032
+ async handleApi(req, res) {
3033
+ const authorized = await this.nodescope.checkAuthorization(req);
3034
+ if (!authorized) {
3035
+ res.status(403).json({ error: "Unauthorized" });
3036
+ return;
3037
+ }
3038
+ const apiPath = req.path.replace(this.nodescope.dashboardPath, "");
3039
+ const response = await this.nodescope.api.handle({
3040
+ method: req.method,
3041
+ url: apiPath,
3042
+ query: req.query,
3043
+ body: req.body
3044
+ });
3045
+ if (response.headers) {
3046
+ for (const [key, value] of Object.entries(response.headers)) {
3047
+ res.setHeader(key, value);
3048
+ }
3049
+ }
3050
+ res.status(response.status).json(response.body);
3051
+ }
3052
+ };
3053
+ var NodeScopeModule = class _NodeScopeModule {
3054
+ /**
3055
+ * Create a dynamic NestJS module for NodeScope
3056
+ *
3057
+ * @example
3058
+ * ```typescript
3059
+ * import { Module } from '@nestjs/common';
3060
+ * import { NodeScopeModule } from '@vipin733/nodescope';
3061
+ *
3062
+ * @Module({
3063
+ * imports: [
3064
+ * NodeScopeModule.forRoot({
3065
+ * storage: 'sqlite',
3066
+ * dashboardPath: '/_debug',
3067
+ * }),
3068
+ * ],
3069
+ * })
3070
+ * export class AppModule {}
3071
+ * ```
3072
+ */
3073
+ static forRoot(config = {}) {
3074
+ const nodescope2 = new NodeScope(config);
3075
+ nodescope2.initialize().catch((err) => {
3076
+ console.error("Failed to initialize NodeScope:", err);
3077
+ });
3078
+ return {
3079
+ module: _NodeScopeModule,
3080
+ providers: [
3081
+ {
3082
+ provide: "NODESCOPE_INSTANCE",
3083
+ useValue: nodescope2
3084
+ },
3085
+ {
3086
+ provide: NodeScope,
3087
+ useValue: nodescope2
3088
+ },
3089
+ {
3090
+ provide: NodeScopeMiddleware,
3091
+ useFactory: () => new NodeScopeMiddleware(nodescope2)
3092
+ },
3093
+ {
3094
+ provide: NodeScopeInterceptor,
3095
+ useFactory: () => new NodeScopeInterceptor(nodescope2)
3096
+ },
3097
+ {
3098
+ provide: NodeScopeController,
3099
+ useFactory: () => new NodeScopeController(nodescope2)
3100
+ }
3101
+ ],
3102
+ exports: ["NODESCOPE_INSTANCE", NodeScope, NodeScopeMiddleware, NodeScopeInterceptor],
3103
+ global: true
3104
+ };
3105
+ }
3106
+ /**
3107
+ * Create an async dynamic module
3108
+ * Useful when you need to inject other services
3109
+ *
3110
+ * @example
3111
+ * ```typescript
3112
+ * NodeScopeModule.forRootAsync({
3113
+ * useFactory: (configService: ConfigService) => ({
3114
+ * storage: configService.get('NODESCOPE_STORAGE'),
3115
+ * dashboardPath: '/_debug',
3116
+ * enabled: configService.get('NODE_ENV') === 'development',
3117
+ * }),
3118
+ * inject: [ConfigService],
3119
+ * })
3120
+ * ```
3121
+ */
3122
+ static forRootAsync(options) {
3123
+ return {
3124
+ module: _NodeScopeModule,
3125
+ providers: [
3126
+ {
3127
+ provide: "NODESCOPE_CONFIG",
3128
+ useFactory: options.useFactory,
3129
+ inject: options.inject || []
3130
+ },
3131
+ {
3132
+ provide: "NODESCOPE_INSTANCE",
3133
+ useFactory: async (config) => {
3134
+ const nodescope2 = new NodeScope(config);
3135
+ await nodescope2.initialize();
3136
+ return nodescope2;
3137
+ },
3138
+ inject: ["NODESCOPE_CONFIG"]
3139
+ },
3140
+ {
3141
+ provide: NodeScope,
3142
+ useFactory: async (config) => {
3143
+ const nodescope2 = new NodeScope(config);
3144
+ await nodescope2.initialize();
3145
+ return nodescope2;
3146
+ },
3147
+ inject: ["NODESCOPE_CONFIG"]
3148
+ },
3149
+ {
3150
+ provide: NodeScopeMiddleware,
3151
+ useFactory: (nodescope2) => new NodeScopeMiddleware(nodescope2),
3152
+ inject: ["NODESCOPE_INSTANCE"]
3153
+ },
3154
+ {
3155
+ provide: NodeScopeInterceptor,
3156
+ useFactory: (nodescope2) => new NodeScopeInterceptor(nodescope2),
3157
+ inject: ["NODESCOPE_INSTANCE"]
3158
+ },
3159
+ {
3160
+ provide: NodeScopeController,
3161
+ useFactory: (nodescope2) => new NodeScopeController(nodescope2),
3162
+ inject: ["NODESCOPE_INSTANCE"]
3163
+ }
3164
+ ],
3165
+ exports: ["NODESCOPE_INSTANCE", NodeScope, NodeScopeMiddleware, NodeScopeInterceptor],
3166
+ global: true
3167
+ };
3168
+ }
3169
+ };
3170
+ async function setupNodeScopeRoutes(app, nodescope2) {
3171
+ const dashboardPath = nodescope2.dashboardPath;
3172
+ const controller = new NodeScopeController(nodescope2);
3173
+ app.getHttpAdapter().get(dashboardPath, (req, res) => {
3174
+ return controller.getDashboard(req, res);
3175
+ });
3176
+ app.getHttpAdapter().all(`${dashboardPath}/api/*`, (req, res) => {
3177
+ return controller.handleApi(req, res);
3178
+ });
3179
+ const httpServer = app.getHttpServer();
3180
+ if (httpServer) {
3181
+ import("ws").then(({ WebSocketServer }) => {
3182
+ const wss = new WebSocketServer({ noServer: true });
3183
+ const wsPath = `${dashboardPath}/ws`;
3184
+ httpServer.on("upgrade", (request, socket, head) => {
3185
+ const url = new URL(request.url || "", `http://${request.headers.host}`);
3186
+ if (url.pathname === wsPath) {
3187
+ wss.handleUpgrade(request, socket, head, (ws) => {
3188
+ nodescope2.realtime.handleConnection(ws);
3189
+ ws.on("close", () => {
3190
+ nodescope2.realtime.handleDisconnection(ws);
3191
+ });
3192
+ });
3193
+ }
3194
+ });
3195
+ console.log("[NodeScope] WebSocket server attached for real-time updates at", wsPath);
3196
+ }).catch((err) => {
3197
+ console.warn("[NodeScope] WebSocket not available, real-time updates disabled:", err.message);
3198
+ });
3199
+ }
3200
+ }
2885
3201
  // Annotate the CommonJS export names for ESM import in node:
2886
3202
  0 && (module.exports = {
2887
3203
  ApiHandler,
@@ -2895,6 +3211,10 @@ async function fastifyNodeScope(fastify, options) {
2895
3211
  MemoryStorage,
2896
3212
  MySQLStorage,
2897
3213
  NodeScope,
3214
+ NodeScopeController,
3215
+ NodeScopeInterceptor,
3216
+ NodeScopeMiddleware,
3217
+ NodeScopeModule,
2898
3218
  PostgreSQLStorage,
2899
3219
  QueryWatcher,
2900
3220
  RealTimeServer,
@@ -2920,6 +3240,7 @@ async function fastifyNodeScope(fastify, options) {
2920
3240
  mountExpressRoutes,
2921
3241
  nodescope,
2922
3242
  setupGlobalErrorHandlers,
3243
+ setupNodeScopeRoutes,
2923
3244
  wrapFetch,
2924
3245
  wrapJobProcessor,
2925
3246
  wrapPrisma
package/dist/index.d.cts CHANGED
@@ -1,6 +1,8 @@
1
1
  import * as http from 'http';
2
2
  import { RequestHandler } from 'express';
3
3
  import { Hono, MiddlewareHandler } from 'hono';
4
+ import { ExecutionContext, CallHandler, NestMiddleware, DynamicModule } from '@nestjs/common';
5
+ import { Observable } from 'rxjs';
4
6
 
5
7
  /**
6
8
  * Entry types that NodeScope can capture and display
@@ -919,10 +921,107 @@ declare function fastifyNodeScope(fastify: FastifyInstance, options: {
919
921
  nodescope: NodeScope;
920
922
  }): Promise<void>;
921
923
 
924
+ /**
925
+ * NodeScope Middleware for NestJS
926
+ * Tracks HTTP requests and responses
927
+ *
928
+ * This middleware must be provided by NodeScopeModule to ensure proper DI
929
+ */
930
+ declare class NodeScopeMiddleware implements NestMiddleware {
931
+ private readonly nodescope;
932
+ constructor(nodescope: NodeScope);
933
+ use(req: any, res: any, next: () => void): Promise<void>;
934
+ }
935
+ /**
936
+ * NodeScope Interceptor for NestJS
937
+ * Alternative to middleware, provides more NestJS-native integration
938
+ */
939
+ declare class NodeScopeInterceptor {
940
+ private readonly nodescope;
941
+ constructor(nodescope: NodeScope);
942
+ intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>>;
943
+ }
944
+ /**
945
+ * NodeScope Controller for NestJS
946
+ * Serves the dashboard and API endpoints
947
+ */
948
+ declare class NodeScopeController {
949
+ private readonly nodescope;
950
+ constructor(nodescope: NodeScope);
951
+ getDashboard(req: any, res: any): Promise<any>;
952
+ handleApi(req: any, res: any): Promise<any>;
953
+ }
954
+ /**
955
+ * NodeScope Module for NestJS
956
+ * Dynamic module that provides NodeScope functionality
957
+ */
958
+ declare class NodeScopeModule {
959
+ /**
960
+ * Create a dynamic NestJS module for NodeScope
961
+ *
962
+ * @example
963
+ * ```typescript
964
+ * import { Module } from '@nestjs/common';
965
+ * import { NodeScopeModule } from '@vipin733/nodescope';
966
+ *
967
+ * @Module({
968
+ * imports: [
969
+ * NodeScopeModule.forRoot({
970
+ * storage: 'sqlite',
971
+ * dashboardPath: '/_debug',
972
+ * }),
973
+ * ],
974
+ * })
975
+ * export class AppModule {}
976
+ * ```
977
+ */
978
+ static forRoot(config?: NodeScopeConfig): DynamicModule;
979
+ /**
980
+ * Create an async dynamic module
981
+ * Useful when you need to inject other services
982
+ *
983
+ * @example
984
+ * ```typescript
985
+ * NodeScopeModule.forRootAsync({
986
+ * useFactory: (configService: ConfigService) => ({
987
+ * storage: configService.get('NODESCOPE_STORAGE'),
988
+ * dashboardPath: '/_debug',
989
+ * enabled: configService.get('NODE_ENV') === 'development',
990
+ * }),
991
+ * inject: [ConfigService],
992
+ * })
993
+ * ```
994
+ */
995
+ static forRootAsync(options: {
996
+ useFactory: (...args: any[]) => NodeScopeConfig | Promise<NodeScopeConfig>;
997
+ inject?: any[];
998
+ }): DynamicModule;
999
+ }
1000
+ /**
1001
+ * Helper function to configure NestJS routes for NodeScope dashboard
1002
+ * Use this in your main.ts to set up dashboard routes
1003
+ *
1004
+ * @example
1005
+ * ```typescript
1006
+ * import { setupNodeScopeRoutes } from '@vipin733/nodescope';
1007
+ *
1008
+ * async function bootstrap() {
1009
+ * const app = await NestFactory.create(AppModule);
1010
+ *
1011
+ * // Get NodeScope instance from app
1012
+ * const nodescope = app.get('NODESCOPE_INSTANCE');
1013
+ * await setupNodeScopeRoutes(app, nodescope);
1014
+ *
1015
+ * await app.listen(3000);
1016
+ * }
1017
+ * ```
1018
+ */
1019
+ declare function setupNodeScopeRoutes(app: any, nodescope: NodeScope): Promise<void>;
1020
+
922
1021
  /**
923
1022
  * Get the embedded dashboard HTML
924
1023
  * This is a simple HTML page that loads the dashboard React app
925
1024
  */
926
1025
  declare function getDashboardHtml(basePath: string): string;
927
1026
 
928
- export { ApiHandler, BaseWatcher, type CacheEntryContent, type CacheOperation, CacheWatcher, type CacheWatcherOptions, type DumpEntryContent, type Entry, type EntryType, type EventEntryContent, EventWatcher, type EventWatcherOptions, type ExceptionEntryContent, ExceptionWatcher, type ExceptionWatcherOptions, type HttpClientEntryContent, HttpClientWatcher, type HttpClientWatcherOptions, type HttpMethod, type JobEntryContent, type JobStatus, JobWatcher, type JobWatcherOptions, type ListOptions, type LogEntryContent, type LogLevel, LogWatcher, type LogWatcherOptions, MemoryStorage, MySQLStorage, NodeScope, type NodeScopeConfig, type PaginatedResult, PostgreSQLStorage, type QueryEntryContent, QueryWatcher, type QueryWatcherOptions, RealTimeServer, type RequestContext, type RequestEntryContent, RequestWatcher, type RequestWatcherOptions, SQLiteStorage, type ScheduleEntryContent, type ScheduleWatcherOptions, type StorageAdapter, type StorageDriver, type StorageStats, TrackedEventEmitter, type WatcherConfig, attachWebSocket, createCacheWrapper, createExpressMiddleware, createHonoDashboardRoutes, createHonoMiddleware, createQueryInterceptor, createRequestContext, createStorageAdapter, fastifyNodeScope, getDashboardHtml, getDuration, getMemoryDelta, getNodeScope, initNodeScope, interceptConsole, interceptFetch, mountExpressRoutes, nodescope, setupGlobalErrorHandlers, wrapFetch, wrapJobProcessor, wrapPrisma };
1027
+ export { ApiHandler, BaseWatcher, type CacheEntryContent, type CacheOperation, CacheWatcher, type CacheWatcherOptions, type DumpEntryContent, type Entry, type EntryType, type EventEntryContent, EventWatcher, type EventWatcherOptions, type ExceptionEntryContent, ExceptionWatcher, type ExceptionWatcherOptions, type HttpClientEntryContent, HttpClientWatcher, type HttpClientWatcherOptions, type HttpMethod, type JobEntryContent, type JobStatus, JobWatcher, type JobWatcherOptions, type ListOptions, type LogEntryContent, type LogLevel, LogWatcher, type LogWatcherOptions, MemoryStorage, MySQLStorage, NodeScope, type NodeScopeConfig, NodeScopeController, NodeScopeInterceptor, NodeScopeMiddleware, NodeScopeModule, type PaginatedResult, PostgreSQLStorage, type QueryEntryContent, QueryWatcher, type QueryWatcherOptions, RealTimeServer, type RequestContext, type RequestEntryContent, RequestWatcher, type RequestWatcherOptions, SQLiteStorage, type ScheduleEntryContent, type ScheduleWatcherOptions, type StorageAdapter, type StorageDriver, type StorageStats, TrackedEventEmitter, type WatcherConfig, attachWebSocket, createCacheWrapper, createExpressMiddleware, createHonoDashboardRoutes, createHonoMiddleware, createQueryInterceptor, createRequestContext, createStorageAdapter, fastifyNodeScope, getDashboardHtml, getDuration, getMemoryDelta, getNodeScope, initNodeScope, interceptConsole, interceptFetch, mountExpressRoutes, nodescope, setupGlobalErrorHandlers, setupNodeScopeRoutes, wrapFetch, wrapJobProcessor, wrapPrisma };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  import * as http from 'http';
2
2
  import { RequestHandler } from 'express';
3
3
  import { Hono, MiddlewareHandler } from 'hono';
4
+ import { ExecutionContext, CallHandler, NestMiddleware, DynamicModule } from '@nestjs/common';
5
+ import { Observable } from 'rxjs';
4
6
 
5
7
  /**
6
8
  * Entry types that NodeScope can capture and display
@@ -919,10 +921,107 @@ declare function fastifyNodeScope(fastify: FastifyInstance, options: {
919
921
  nodescope: NodeScope;
920
922
  }): Promise<void>;
921
923
 
924
+ /**
925
+ * NodeScope Middleware for NestJS
926
+ * Tracks HTTP requests and responses
927
+ *
928
+ * This middleware must be provided by NodeScopeModule to ensure proper DI
929
+ */
930
+ declare class NodeScopeMiddleware implements NestMiddleware {
931
+ private readonly nodescope;
932
+ constructor(nodescope: NodeScope);
933
+ use(req: any, res: any, next: () => void): Promise<void>;
934
+ }
935
+ /**
936
+ * NodeScope Interceptor for NestJS
937
+ * Alternative to middleware, provides more NestJS-native integration
938
+ */
939
+ declare class NodeScopeInterceptor {
940
+ private readonly nodescope;
941
+ constructor(nodescope: NodeScope);
942
+ intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>>;
943
+ }
944
+ /**
945
+ * NodeScope Controller for NestJS
946
+ * Serves the dashboard and API endpoints
947
+ */
948
+ declare class NodeScopeController {
949
+ private readonly nodescope;
950
+ constructor(nodescope: NodeScope);
951
+ getDashboard(req: any, res: any): Promise<any>;
952
+ handleApi(req: any, res: any): Promise<any>;
953
+ }
954
+ /**
955
+ * NodeScope Module for NestJS
956
+ * Dynamic module that provides NodeScope functionality
957
+ */
958
+ declare class NodeScopeModule {
959
+ /**
960
+ * Create a dynamic NestJS module for NodeScope
961
+ *
962
+ * @example
963
+ * ```typescript
964
+ * import { Module } from '@nestjs/common';
965
+ * import { NodeScopeModule } from '@vipin733/nodescope';
966
+ *
967
+ * @Module({
968
+ * imports: [
969
+ * NodeScopeModule.forRoot({
970
+ * storage: 'sqlite',
971
+ * dashboardPath: '/_debug',
972
+ * }),
973
+ * ],
974
+ * })
975
+ * export class AppModule {}
976
+ * ```
977
+ */
978
+ static forRoot(config?: NodeScopeConfig): DynamicModule;
979
+ /**
980
+ * Create an async dynamic module
981
+ * Useful when you need to inject other services
982
+ *
983
+ * @example
984
+ * ```typescript
985
+ * NodeScopeModule.forRootAsync({
986
+ * useFactory: (configService: ConfigService) => ({
987
+ * storage: configService.get('NODESCOPE_STORAGE'),
988
+ * dashboardPath: '/_debug',
989
+ * enabled: configService.get('NODE_ENV') === 'development',
990
+ * }),
991
+ * inject: [ConfigService],
992
+ * })
993
+ * ```
994
+ */
995
+ static forRootAsync(options: {
996
+ useFactory: (...args: any[]) => NodeScopeConfig | Promise<NodeScopeConfig>;
997
+ inject?: any[];
998
+ }): DynamicModule;
999
+ }
1000
+ /**
1001
+ * Helper function to configure NestJS routes for NodeScope dashboard
1002
+ * Use this in your main.ts to set up dashboard routes
1003
+ *
1004
+ * @example
1005
+ * ```typescript
1006
+ * import { setupNodeScopeRoutes } from '@vipin733/nodescope';
1007
+ *
1008
+ * async function bootstrap() {
1009
+ * const app = await NestFactory.create(AppModule);
1010
+ *
1011
+ * // Get NodeScope instance from app
1012
+ * const nodescope = app.get('NODESCOPE_INSTANCE');
1013
+ * await setupNodeScopeRoutes(app, nodescope);
1014
+ *
1015
+ * await app.listen(3000);
1016
+ * }
1017
+ * ```
1018
+ */
1019
+ declare function setupNodeScopeRoutes(app: any, nodescope: NodeScope): Promise<void>;
1020
+
922
1021
  /**
923
1022
  * Get the embedded dashboard HTML
924
1023
  * This is a simple HTML page that loads the dashboard React app
925
1024
  */
926
1025
  declare function getDashboardHtml(basePath: string): string;
927
1026
 
928
- export { ApiHandler, BaseWatcher, type CacheEntryContent, type CacheOperation, CacheWatcher, type CacheWatcherOptions, type DumpEntryContent, type Entry, type EntryType, type EventEntryContent, EventWatcher, type EventWatcherOptions, type ExceptionEntryContent, ExceptionWatcher, type ExceptionWatcherOptions, type HttpClientEntryContent, HttpClientWatcher, type HttpClientWatcherOptions, type HttpMethod, type JobEntryContent, type JobStatus, JobWatcher, type JobWatcherOptions, type ListOptions, type LogEntryContent, type LogLevel, LogWatcher, type LogWatcherOptions, MemoryStorage, MySQLStorage, NodeScope, type NodeScopeConfig, type PaginatedResult, PostgreSQLStorage, type QueryEntryContent, QueryWatcher, type QueryWatcherOptions, RealTimeServer, type RequestContext, type RequestEntryContent, RequestWatcher, type RequestWatcherOptions, SQLiteStorage, type ScheduleEntryContent, type ScheduleWatcherOptions, type StorageAdapter, type StorageDriver, type StorageStats, TrackedEventEmitter, type WatcherConfig, attachWebSocket, createCacheWrapper, createExpressMiddleware, createHonoDashboardRoutes, createHonoMiddleware, createQueryInterceptor, createRequestContext, createStorageAdapter, fastifyNodeScope, getDashboardHtml, getDuration, getMemoryDelta, getNodeScope, initNodeScope, interceptConsole, interceptFetch, mountExpressRoutes, nodescope, setupGlobalErrorHandlers, wrapFetch, wrapJobProcessor, wrapPrisma };
1027
+ export { ApiHandler, BaseWatcher, type CacheEntryContent, type CacheOperation, CacheWatcher, type CacheWatcherOptions, type DumpEntryContent, type Entry, type EntryType, type EventEntryContent, EventWatcher, type EventWatcherOptions, type ExceptionEntryContent, ExceptionWatcher, type ExceptionWatcherOptions, type HttpClientEntryContent, HttpClientWatcher, type HttpClientWatcherOptions, type HttpMethod, type JobEntryContent, type JobStatus, JobWatcher, type JobWatcherOptions, type ListOptions, type LogEntryContent, type LogLevel, LogWatcher, type LogWatcherOptions, MemoryStorage, MySQLStorage, NodeScope, type NodeScopeConfig, NodeScopeController, NodeScopeInterceptor, NodeScopeMiddleware, NodeScopeModule, type PaginatedResult, PostgreSQLStorage, type QueryEntryContent, QueryWatcher, type QueryWatcherOptions, RealTimeServer, type RequestContext, type RequestEntryContent, RequestWatcher, type RequestWatcherOptions, SQLiteStorage, type ScheduleEntryContent, type ScheduleWatcherOptions, type StorageAdapter, type StorageDriver, type StorageStats, TrackedEventEmitter, type WatcherConfig, attachWebSocket, createCacheWrapper, createExpressMiddleware, createHonoDashboardRoutes, createHonoMiddleware, createQueryInterceptor, createRequestContext, createStorageAdapter, fastifyNodeScope, getDashboardHtml, getDuration, getMemoryDelta, getNodeScope, initNodeScope, interceptConsole, interceptFetch, mountExpressRoutes, nodescope, setupGlobalErrorHandlers, setupNodeScopeRoutes, wrapFetch, wrapJobProcessor, wrapPrisma };
package/dist/index.js CHANGED
@@ -2049,6 +2049,317 @@ async function fastifyNodeScope(fastify, options) {
2049
2049
  return reply.status(response.status).send(response.body);
2050
2050
  });
2051
2051
  }
2052
+
2053
+ // src/adapters/nestjs.ts
2054
+ var NodeScopeMiddleware = class {
2055
+ constructor(nodescope2) {
2056
+ this.nodescope = nodescope2;
2057
+ }
2058
+ async use(req, res, next) {
2059
+ const nodescope2 = this.nodescope;
2060
+ if (!nodescope2 || !nodescope2.isEnabled) {
2061
+ return next();
2062
+ }
2063
+ const dashboardPath = nodescope2.dashboardPath;
2064
+ if (req.path?.startsWith(dashboardPath)) {
2065
+ return next();
2066
+ }
2067
+ const ctx = nodescope2.createContext();
2068
+ req.nodescope = ctx;
2069
+ const originalSend = res.send;
2070
+ const originalJson = res.json;
2071
+ let responseBody;
2072
+ res.send = function(body) {
2073
+ responseBody = body;
2074
+ return originalSend.call(this, body);
2075
+ };
2076
+ res.json = function(body) {
2077
+ responseBody = body;
2078
+ return originalJson.call(this, body);
2079
+ };
2080
+ res.on("finish", async () => {
2081
+ if (!nodescope2.requestWatcher.enabled) return;
2082
+ try {
2083
+ const entry = nodescope2.requestWatcher.record({
2084
+ batchId: ctx.batchId,
2085
+ startTime: ctx.startTime,
2086
+ method: req.method,
2087
+ url: req.originalUrl || req.url,
2088
+ path: req.path || req.route?.path,
2089
+ query: req.query,
2090
+ headers: req.headers,
2091
+ body: req.body,
2092
+ ip: req.ip || req.socket?.remoteAddress,
2093
+ userAgent: req.get?.("user-agent"),
2094
+ session: req.session,
2095
+ response: {
2096
+ status: res.statusCode,
2097
+ headers: res.getHeaders?.() || {},
2098
+ body: responseBody
2099
+ }
2100
+ });
2101
+ if (entry) {
2102
+ await nodescope2.recordEntry(entry);
2103
+ }
2104
+ } catch (error) {
2105
+ }
2106
+ });
2107
+ next();
2108
+ }
2109
+ };
2110
+ var NodeScopeInterceptor = class {
2111
+ constructor(nodescope2) {
2112
+ this.nodescope = nodescope2;
2113
+ }
2114
+ async intercept(context, next) {
2115
+ if (!this.nodescope.isEnabled) {
2116
+ return next.handle();
2117
+ }
2118
+ const http = context.switchToHttp();
2119
+ const request = http.getRequest();
2120
+ const response = http.getResponse();
2121
+ const dashboardPath = this.nodescope.dashboardPath;
2122
+ if (request.path?.startsWith(dashboardPath)) {
2123
+ return next.handle();
2124
+ }
2125
+ const ctx = this.nodescope.createContext();
2126
+ request.nodescope = ctx;
2127
+ const { tap } = await import("rxjs/operators");
2128
+ return next.handle().pipe(
2129
+ tap({
2130
+ next: async (data) => {
2131
+ if (!this.nodescope.requestWatcher.enabled) return;
2132
+ try {
2133
+ const entry = this.nodescope.requestWatcher.record({
2134
+ batchId: ctx.batchId,
2135
+ startTime: ctx.startTime,
2136
+ method: request.method,
2137
+ url: request.originalUrl || request.url,
2138
+ path: request.path || request.route?.path,
2139
+ query: request.query,
2140
+ headers: request.headers,
2141
+ body: request.body,
2142
+ ip: request.ip || request.socket?.remoteAddress,
2143
+ userAgent: request.get?.("user-agent"),
2144
+ session: request.session,
2145
+ response: {
2146
+ status: response.statusCode,
2147
+ headers: response.getHeaders?.() || {},
2148
+ body: data
2149
+ }
2150
+ });
2151
+ if (entry) {
2152
+ await this.nodescope.recordEntry(entry);
2153
+ }
2154
+ } catch (error) {
2155
+ console.error("NodeScope error recording request:", error);
2156
+ }
2157
+ },
2158
+ error: async (error) => {
2159
+ if (!this.nodescope.exceptionWatcher.enabled) return;
2160
+ try {
2161
+ const errorObj = error instanceof Error ? error : new Error(String(error));
2162
+ const entry = this.nodescope.exceptionWatcher.record({
2163
+ error: errorObj,
2164
+ context: {
2165
+ method: request.method,
2166
+ url: request.url,
2167
+ statusCode: error.status || 500
2168
+ }
2169
+ });
2170
+ if (entry) {
2171
+ await this.nodescope.recordEntry(entry);
2172
+ }
2173
+ } catch (recordError) {
2174
+ console.error("NodeScope error recording exception:", recordError);
2175
+ }
2176
+ }
2177
+ })
2178
+ );
2179
+ }
2180
+ };
2181
+ var NodeScopeController = class {
2182
+ constructor(nodescope2) {
2183
+ this.nodescope = nodescope2;
2184
+ }
2185
+ async getDashboard(req, res) {
2186
+ const authorized = await this.nodescope.checkAuthorization(req);
2187
+ if (!authorized) {
2188
+ res.status(403).json({ error: "Unauthorized" });
2189
+ return;
2190
+ }
2191
+ res.setHeader("Content-Type", "text/html");
2192
+ res.send(getDashboardHtml(this.nodescope.dashboardPath));
2193
+ }
2194
+ async handleApi(req, res) {
2195
+ const authorized = await this.nodescope.checkAuthorization(req);
2196
+ if (!authorized) {
2197
+ res.status(403).json({ error: "Unauthorized" });
2198
+ return;
2199
+ }
2200
+ const apiPath = req.path.replace(this.nodescope.dashboardPath, "");
2201
+ const response = await this.nodescope.api.handle({
2202
+ method: req.method,
2203
+ url: apiPath,
2204
+ query: req.query,
2205
+ body: req.body
2206
+ });
2207
+ if (response.headers) {
2208
+ for (const [key, value] of Object.entries(response.headers)) {
2209
+ res.setHeader(key, value);
2210
+ }
2211
+ }
2212
+ res.status(response.status).json(response.body);
2213
+ }
2214
+ };
2215
+ var NodeScopeModule = class _NodeScopeModule {
2216
+ /**
2217
+ * Create a dynamic NestJS module for NodeScope
2218
+ *
2219
+ * @example
2220
+ * ```typescript
2221
+ * import { Module } from '@nestjs/common';
2222
+ * import { NodeScopeModule } from '@vipin733/nodescope';
2223
+ *
2224
+ * @Module({
2225
+ * imports: [
2226
+ * NodeScopeModule.forRoot({
2227
+ * storage: 'sqlite',
2228
+ * dashboardPath: '/_debug',
2229
+ * }),
2230
+ * ],
2231
+ * })
2232
+ * export class AppModule {}
2233
+ * ```
2234
+ */
2235
+ static forRoot(config = {}) {
2236
+ const nodescope2 = new NodeScope(config);
2237
+ nodescope2.initialize().catch((err) => {
2238
+ console.error("Failed to initialize NodeScope:", err);
2239
+ });
2240
+ return {
2241
+ module: _NodeScopeModule,
2242
+ providers: [
2243
+ {
2244
+ provide: "NODESCOPE_INSTANCE",
2245
+ useValue: nodescope2
2246
+ },
2247
+ {
2248
+ provide: NodeScope,
2249
+ useValue: nodescope2
2250
+ },
2251
+ {
2252
+ provide: NodeScopeMiddleware,
2253
+ useFactory: () => new NodeScopeMiddleware(nodescope2)
2254
+ },
2255
+ {
2256
+ provide: NodeScopeInterceptor,
2257
+ useFactory: () => new NodeScopeInterceptor(nodescope2)
2258
+ },
2259
+ {
2260
+ provide: NodeScopeController,
2261
+ useFactory: () => new NodeScopeController(nodescope2)
2262
+ }
2263
+ ],
2264
+ exports: ["NODESCOPE_INSTANCE", NodeScope, NodeScopeMiddleware, NodeScopeInterceptor],
2265
+ global: true
2266
+ };
2267
+ }
2268
+ /**
2269
+ * Create an async dynamic module
2270
+ * Useful when you need to inject other services
2271
+ *
2272
+ * @example
2273
+ * ```typescript
2274
+ * NodeScopeModule.forRootAsync({
2275
+ * useFactory: (configService: ConfigService) => ({
2276
+ * storage: configService.get('NODESCOPE_STORAGE'),
2277
+ * dashboardPath: '/_debug',
2278
+ * enabled: configService.get('NODE_ENV') === 'development',
2279
+ * }),
2280
+ * inject: [ConfigService],
2281
+ * })
2282
+ * ```
2283
+ */
2284
+ static forRootAsync(options) {
2285
+ return {
2286
+ module: _NodeScopeModule,
2287
+ providers: [
2288
+ {
2289
+ provide: "NODESCOPE_CONFIG",
2290
+ useFactory: options.useFactory,
2291
+ inject: options.inject || []
2292
+ },
2293
+ {
2294
+ provide: "NODESCOPE_INSTANCE",
2295
+ useFactory: async (config) => {
2296
+ const nodescope2 = new NodeScope(config);
2297
+ await nodescope2.initialize();
2298
+ return nodescope2;
2299
+ },
2300
+ inject: ["NODESCOPE_CONFIG"]
2301
+ },
2302
+ {
2303
+ provide: NodeScope,
2304
+ useFactory: async (config) => {
2305
+ const nodescope2 = new NodeScope(config);
2306
+ await nodescope2.initialize();
2307
+ return nodescope2;
2308
+ },
2309
+ inject: ["NODESCOPE_CONFIG"]
2310
+ },
2311
+ {
2312
+ provide: NodeScopeMiddleware,
2313
+ useFactory: (nodescope2) => new NodeScopeMiddleware(nodescope2),
2314
+ inject: ["NODESCOPE_INSTANCE"]
2315
+ },
2316
+ {
2317
+ provide: NodeScopeInterceptor,
2318
+ useFactory: (nodescope2) => new NodeScopeInterceptor(nodescope2),
2319
+ inject: ["NODESCOPE_INSTANCE"]
2320
+ },
2321
+ {
2322
+ provide: NodeScopeController,
2323
+ useFactory: (nodescope2) => new NodeScopeController(nodescope2),
2324
+ inject: ["NODESCOPE_INSTANCE"]
2325
+ }
2326
+ ],
2327
+ exports: ["NODESCOPE_INSTANCE", NodeScope, NodeScopeMiddleware, NodeScopeInterceptor],
2328
+ global: true
2329
+ };
2330
+ }
2331
+ };
2332
+ async function setupNodeScopeRoutes(app, nodescope2) {
2333
+ const dashboardPath = nodescope2.dashboardPath;
2334
+ const controller = new NodeScopeController(nodescope2);
2335
+ app.getHttpAdapter().get(dashboardPath, (req, res) => {
2336
+ return controller.getDashboard(req, res);
2337
+ });
2338
+ app.getHttpAdapter().all(`${dashboardPath}/api/*`, (req, res) => {
2339
+ return controller.handleApi(req, res);
2340
+ });
2341
+ const httpServer = app.getHttpServer();
2342
+ if (httpServer) {
2343
+ import("ws").then(({ WebSocketServer }) => {
2344
+ const wss = new WebSocketServer({ noServer: true });
2345
+ const wsPath = `${dashboardPath}/ws`;
2346
+ httpServer.on("upgrade", (request, socket, head) => {
2347
+ const url = new URL(request.url || "", `http://${request.headers.host}`);
2348
+ if (url.pathname === wsPath) {
2349
+ wss.handleUpgrade(request, socket, head, (ws) => {
2350
+ nodescope2.realtime.handleConnection(ws);
2351
+ ws.on("close", () => {
2352
+ nodescope2.realtime.handleDisconnection(ws);
2353
+ });
2354
+ });
2355
+ }
2356
+ });
2357
+ console.log("[NodeScope] WebSocket server attached for real-time updates at", wsPath);
2358
+ }).catch((err) => {
2359
+ console.warn("[NodeScope] WebSocket not available, real-time updates disabled:", err.message);
2360
+ });
2361
+ }
2362
+ }
2052
2363
  export {
2053
2364
  ApiHandler,
2054
2365
  BaseWatcher,
@@ -2061,6 +2372,10 @@ export {
2061
2372
  MemoryStorage,
2062
2373
  MySQLStorage,
2063
2374
  NodeScope,
2375
+ NodeScopeController,
2376
+ NodeScopeInterceptor,
2377
+ NodeScopeMiddleware,
2378
+ NodeScopeModule,
2064
2379
  PostgreSQLStorage,
2065
2380
  QueryWatcher,
2066
2381
  RealTimeServer,
@@ -2086,6 +2401,7 @@ export {
2086
2401
  mountExpressRoutes,
2087
2402
  nodescope,
2088
2403
  setupGlobalErrorHandlers,
2404
+ setupNodeScopeRoutes,
2089
2405
  wrapFetch,
2090
2406
  wrapJobProcessor,
2091
2407
  wrapPrisma
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vipin733/nodescope",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "A Laravel Telescope-inspired debugging and monitoring package for Node.js and Bun applications",
6
6
  "author": "Vipin",
@@ -21,8 +21,8 @@
21
21
  "LICENSE"
22
22
  ],
23
23
  "scripts": {
24
- "build": "tsup src/index.ts --format cjs,esm --dts --clean --external better-sqlite3 --external pg --external mysql2 --external express --external hono --external fastify",
25
- "dev": "tsup src/index.ts --format cjs,esm --dts --watch --external better-sqlite3 --external pg --external mysql2 --external express --external hono --external fastify",
24
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean --external better-sqlite3 --external pg --external mysql2 --external express --external hono --external fastify --external @nestjs/common --external rxjs",
25
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch --external better-sqlite3 --external pg --external mysql2 --external express --external hono --external fastify --external @nestjs/common --external rxjs",
26
26
  "prepublishOnly": "npm run build",
27
27
  "test": "vitest run",
28
28
  "test:watch": "vitest",
@@ -50,6 +50,7 @@
50
50
  "express",
51
51
  "hono",
52
52
  "fastify",
53
+ "nestjs",
53
54
  "observability",
54
55
  "developer-tools",
55
56
  "debug",
@@ -71,6 +72,7 @@
71
72
  "pg": "^8.11.0"
72
73
  },
73
74
  "devDependencies": {
75
+ "@nestjs/common": "^10.0.0",
74
76
  "@types/better-sqlite3": "^7.6.8",
75
77
  "@types/express": "^4.17.21",
76
78
  "@types/node": "^20.11.0",
@@ -81,24 +83,33 @@
81
83
  "express": "^4.18.2",
82
84
  "fastify": "^4.26.0",
83
85
  "hono": "^4.0.0",
86
+ "rxjs": "^7.8.0",
84
87
  "supertest": "^7.2.2",
85
88
  "tsup": "^8.0.0",
86
89
  "typescript": "^5.3.0",
87
90
  "vitest": "^1.2.0"
88
91
  },
89
92
  "peerDependencies": {
93
+ "@nestjs/common": ">=9.0.0",
90
94
  "express": ">=4.0.0",
91
95
  "fastify": ">=4.0.0",
92
- "hono": ">=4.0.0"
96
+ "hono": ">=4.0.0",
97
+ "rxjs": ">=7.0.0"
93
98
  },
94
99
  "peerDependenciesMeta": {
100
+ "@nestjs/common": {
101
+ "optional": true
102
+ },
95
103
  "express": {
96
104
  "optional": true
97
105
  },
106
+ "fastify": {
107
+ "optional": true
108
+ },
98
109
  "hono": {
99
110
  "optional": true
100
111
  },
101
- "fastify": {
112
+ "rxjs": {
102
113
  "optional": true
103
114
  }
104
115
  },