@ngn-net/nestjs-telescope 0.1.9 → 0.1.11

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.
@@ -5,4 +5,6 @@ export declare class TelescopeModule implements OnModuleInit {
5
5
  constructor(moduleRef: ModuleRef);
6
6
  onModuleInit(): void;
7
7
  static forRoot(options?: any): DynamicModule;
8
+ private static registerWatchers;
9
+ static forRootAsync(optionsFactory: any): DynamicModule;
8
10
  }
@@ -13,6 +13,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.TelescopeModule = void 0;
14
14
  // src/telescope.module.ts
15
15
  const common_1 = require("@nestjs/common");
16
+ const serve_static_1 = require("@nestjs/serve-static");
17
+ const path_1 = require("path");
16
18
  const core_1 = require("@nestjs/core");
17
19
  const typeorm_1 = require("@nestjs/typeorm");
18
20
  const jwt_1 = require("@nestjs/jwt");
@@ -76,6 +78,15 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
76
78
  signOptions: { expiresIn: '1d' },
77
79
  }),
78
80
  ];
81
+ // Serve static UI assets if dashboard is enabled (default true)
82
+ if (options.enableDashboard !== false) {
83
+ const staticPath = (0, path_1.join)(__dirname, '..', 'ui');
84
+ const routePath = options.path || 'telescope';
85
+ imports.push(serve_static_1.ServeStaticModule.forRoot({
86
+ rootPath: staticPath,
87
+ serveRoot: `/${routePath}`,
88
+ }));
89
+ }
79
90
  const providers = [
80
91
  {
81
92
  provide: constants_1.TELESCOPE_OPTIONS,
@@ -87,17 +98,14 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
87
98
  ];
88
99
  // Always registered watchers (no external peer dependencies needed)
89
100
  const watchersToRegister = [
90
- { type: entry_type_enum_1.EntryType.REQUEST, watcher: http_request_watcher_1.HttpRequestWatcher },
101
+ { type: entry_type_enum_1.EntryType.REQUEST, watcher: http_request_watcher_1.HttpRequestWatcher, isInterceptor: true },
91
102
  { type: entry_type_enum_1.EntryType.QUERY, watcher: query_watcher_1.QueryWatcher },
92
103
  { type: entry_type_enum_1.EntryType.LOG, watcher: log_watcher_1.LogWatcher },
93
- { type: entry_type_enum_1.EntryType.EXCEPTION, watcher: exception_watcher_1.ExceptionWatcher },
104
+ { type: entry_type_enum_1.EntryType.EXCEPTION, watcher: exception_watcher_1.ExceptionWatcher, isInterceptor: true },
94
105
  { type: entry_type_enum_1.EntryType.REDIS, watcher: redis_watcher_1.RedisWatcher },
95
106
  ];
96
- for (const item of watchersToRegister) {
97
- if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(item.type)) {
98
- providers.push(item.watcher);
99
- }
100
- }
107
+ // Register core watchers
108
+ this.registerWatchers(options, providers);
101
109
  // Optional dependencies checking:
102
110
  // 1. CacheWatcher (@nestjs/cache-manager)
103
111
  let hasCache = false;
@@ -173,6 +181,10 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
173
181
  // Dynamic Route Prefix Setup using Reflect metadata on TelescopeController
174
182
  const path = options.path || 'telescope';
175
183
  Reflect.defineMetadata('path', path, telescope_controller_1.TelescopeController);
184
+ // Ensure static assets are served under the same path when dashboard enabled
185
+ if (options.enableDashboard !== false) {
186
+ // ServeStaticModule already handles static files; no extra code needed here.
187
+ }
176
188
  return {
177
189
  module: TelescopeModule_1,
178
190
  imports,
@@ -181,6 +193,44 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
181
193
  exports: [telescope_service_1.TelescopeService, telescope_repository_service_1.TelescopeRepository],
182
194
  };
183
195
  }
196
+ // Helper to register core watchers
197
+ static registerWatchers(options, providers) {
198
+ const watchersToRegister = [
199
+ { type: entry_type_enum_1.EntryType.REQUEST, watcher: http_request_watcher_1.HttpRequestWatcher, isInterceptor: true },
200
+ { type: entry_type_enum_1.EntryType.QUERY, watcher: query_watcher_1.QueryWatcher },
201
+ { type: entry_type_enum_1.EntryType.LOG, watcher: log_watcher_1.LogWatcher },
202
+ { type: entry_type_enum_1.EntryType.EXCEPTION, watcher: exception_watcher_1.ExceptionWatcher, isInterceptor: true },
203
+ { type: entry_type_enum_1.EntryType.REDIS, watcher: redis_watcher_1.RedisWatcher },
204
+ ];
205
+ for (const item of watchersToRegister) {
206
+ if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(item.type)) {
207
+ if (item.isInterceptor) {
208
+ providers.push({ provide: core_1.APP_INTERCEPTOR, useClass: item.watcher });
209
+ }
210
+ else {
211
+ providers.push(item.watcher);
212
+ }
213
+ }
214
+ }
215
+ }
216
+ static forRootAsync(optionsFactory) {
217
+ // Allows async injection of TelescopeOptions (e.g., from ConfigService)
218
+ return {
219
+ module: TelescopeModule_1,
220
+ imports: [
221
+ typeorm_1.TypeOrmModule.forFeature([telescope_entry_entity_1.TelescopeEntry]),
222
+ jwt_1.JwtModule.registerAsync({
223
+ useFactory: async (options) => ({
224
+ secret: options.jwtSecret || process.env.TELESCOPE_JWT_SECRET || constants_1.DEFAULT_JWT_SECRET,
225
+ signOptions: { expiresIn: '1d' },
226
+ }),
227
+ inject: [optionsFactory],
228
+ }),
229
+ ],
230
+ providers: [],
231
+ controllers: [telescope_controller_1.TelescopeController],
232
+ };
233
+ }
184
234
  };
185
235
  exports.TelescopeModule = TelescopeModule;
186
236
  exports.TelescopeModule = TelescopeModule = TelescopeModule_1 = __decorate([
@@ -7,7 +7,7 @@ export declare class TelescopeService implements OnModuleInit {
7
7
  private readonly repo;
8
8
  private readonly options?;
9
9
  private lastPruneTime;
10
- private recording;
10
+ private readonly asyncLocalStorage;
11
11
  constructor(repo: TelescopeRepository, options?: TelescopeOptions | undefined);
12
12
  onModuleInit(): Promise<void>;
13
13
  /**
@@ -15,13 +15,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.TelescopeService = void 0;
16
16
  // src/telescope.service.ts
17
17
  const common_1 = require("@nestjs/common");
18
+ const async_hooks_1 = require("async_hooks");
18
19
  const telescope_repository_service_1 = require("./storage/telescope-repository.service");
19
20
  const constants_1 = require("./constants");
20
21
  let TelescopeService = class TelescopeService {
21
22
  repo;
22
23
  options;
23
24
  lastPruneTime = 0;
24
- recording = false; // Guard against recursive recording
25
+ asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
25
26
  constructor(repo, options) {
26
27
  this.repo = repo;
27
28
  this.options = options;
@@ -42,12 +43,14 @@ let TelescopeService = class TelescopeService {
42
43
  * Check if a request path should be ignored.
43
44
  */
44
45
  shouldIgnorePath(path) {
45
- if (!path || !this.options?.ignorePaths)
46
+ if (!path)
46
47
  return false;
47
- const telescopePath = this.options.path || 'telescope';
48
+ const telescopePath = this.options?.path || 'telescope';
48
49
  // Always ignore telescope's own routes
49
50
  if (path.startsWith(`/${telescopePath}`))
50
51
  return true;
52
+ if (!this.options?.ignorePaths)
53
+ return false;
51
54
  return this.options.ignorePaths.some((p) => path.startsWith(p));
52
55
  }
53
56
  /**
@@ -67,40 +70,39 @@ let TelescopeService = class TelescopeService {
67
70
  if (!this.isEnabled(entry.type))
68
71
  return;
69
72
  // Guard against recursive calls (e.g., LogWatcher triggering another record)
70
- if (this.recording)
73
+ // using AsyncLocalStorage to avoid blocking concurrent requests
74
+ if (this.asyncLocalStorage.getStore())
71
75
  return;
72
- this.recording = true;
73
- try {
74
- const uuidStr = entry.uuid ?? `${Date.now()}-${Math.floor(Math.random() * 1000000)}`;
75
- const fullEntry = {
76
- uuid: uuidStr,
77
- type: entry.type,
78
- content: entry.content ?? {},
79
- recordedAt: entry.recordedAt ?? new Date(),
80
- familyHash: entry.familyHash ?? null,
81
- batchId: entry.batchId,
82
- };
83
- await this.repo.save(fullEntry);
84
- // Throttled pruning: at most once every PRUNE_THROTTLE_MS
85
- const now = Date.now();
86
- if (now - this.lastPruneTime >= constants_1.PRUNE_THROTTLE_MS) {
87
- this.lastPruneTime = now;
88
- const maxEntries = this.options?.maxEntries ?? constants_1.DEFAULT_MAX_ENTRIES;
89
- try {
90
- await this.repo.prune(maxEntries);
91
- }
92
- catch {
93
- // Ignore pruning errors
76
+ await this.asyncLocalStorage.run(true, async () => {
77
+ try {
78
+ const uuidStr = entry.uuid ?? `${Date.now()}-${Math.floor(Math.random() * 1000000)}`;
79
+ const fullEntry = {
80
+ uuid: uuidStr,
81
+ type: entry.type,
82
+ content: entry.content ?? {},
83
+ recordedAt: entry.recordedAt ?? new Date(),
84
+ familyHash: entry.familyHash ?? null,
85
+ batchId: entry.batchId,
86
+ };
87
+ await this.repo.save(fullEntry);
88
+ // Throttled pruning: at most once every PRUNE_THROTTLE_MS
89
+ const now = Date.now();
90
+ if (now - this.lastPruneTime >= constants_1.PRUNE_THROTTLE_MS) {
91
+ this.lastPruneTime = now;
92
+ const maxEntries = this.options?.maxEntries ?? constants_1.DEFAULT_MAX_ENTRIES;
93
+ try {
94
+ await this.repo.prune(maxEntries);
95
+ }
96
+ catch {
97
+ // Ignore pruning errors
98
+ }
94
99
  }
95
100
  }
96
- }
97
- catch (err) {
98
- // Gracefully catch all database/metadata errors to protect host app stability.
99
- // Avoid using console.log here as it might trigger infinite recursion in LogWatcher.
100
- }
101
- finally {
102
- this.recording = false;
103
- }
101
+ catch (err) {
102
+ // Gracefully catch all database/metadata errors to protect host app stability.
103
+ // Avoid using console.log here as it might trigger infinite recursion in LogWatcher.
104
+ }
105
+ });
104
106
  }
105
107
  };
106
108
  exports.TelescopeService = TelescopeService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngn-net/nestjs-telescope",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -21,25 +21,26 @@
21
21
  "prepare": "npm run build"
22
22
  },
23
23
  "dependencies": {
24
+ "@nestjs/serve-static": "^5.0.5",
24
25
  "class-transformer": "^0.5",
25
26
  "class-validator": "^0.14",
26
- "reflect-metadata": "^0.2",
27
- "passport-jwt": "^4"
27
+ "passport-jwt": "^4",
28
+ "reflect-metadata": "^0.2"
28
29
  },
29
30
  "peerDependencies": {
31
+ "@nestjs/bullmq": "^11",
32
+ "@nestjs/cache-manager": "^3",
30
33
  "@nestjs/common": "^11",
31
34
  "@nestjs/core": "^11",
32
- "@nestjs/typeorm": "^11",
33
- "typeorm": "^0.3",
35
+ "@nestjs/event-emitter": "^2",
34
36
  "@nestjs/jwt": "^11",
35
37
  "@nestjs/passport": "^11",
36
- "@nestjs/bullmq": "^11",
38
+ "@nestjs/schedule": "^4",
39
+ "@nestjs/typeorm": "^11",
37
40
  "bullmq": "^5",
38
- "@nestjs/cache-manager": "^3",
39
41
  "cache-manager": "^5",
40
- "@nestjs/event-emitter": "^2",
41
- "@nestjs/schedule": "^4",
42
- "nodemailer": "^6"
42
+ "nodemailer": "^6",
43
+ "typeorm": "^0.3"
43
44
  },
44
45
  "peerDependenciesMeta": {
45
46
  "@nestjs/bullmq": {
@@ -65,26 +66,26 @@
65
66
  }
66
67
  },
67
68
  "devDependencies": {
69
+ "@nestjs/bullmq": "^11",
70
+ "@nestjs/cache-manager": "^3",
68
71
  "@nestjs/common": "^11",
69
72
  "@nestjs/core": "^11",
70
- "@nestjs/typeorm": "^11",
71
- "@nestjs/bullmq": "^11",
73
+ "@nestjs/event-emitter": "^2",
72
74
  "@nestjs/jwt": "^11",
73
75
  "@nestjs/passport": "^11",
74
- "@nestjs/event-emitter": "^2",
75
76
  "@nestjs/schedule": "^4",
76
- "@nestjs/cache-manager": "^3",
77
- "ts-node": "^10",
78
- "rimraf": "^5",
77
+ "@nestjs/typeorm": "^11",
78
+ "@types/express": "^4.17.21",
79
+ "@types/jest": "^29",
80
+ "@types/node": "^20",
81
+ "@types/nodemailer": "^6.4.15",
79
82
  "jest": "^29",
80
- "ts-jest": "^29",
81
- "typescript": "^5",
83
+ "nodemailer": "^6",
84
+ "rimraf": "^5",
82
85
  "rxjs": "^7",
86
+ "ts-jest": "^29",
87
+ "ts-node": "^10",
83
88
  "typeorm": "^0.3",
84
- "nodemailer": "^6",
85
- "@types/jest": "^29",
86
- "@types/node": "^20",
87
- "@types/express": "^4.17.21",
88
- "@types/nodemailer": "^6.4.15"
89
+ "typescript": "^5"
89
90
  }
90
91
  }