@ngn-net/nestjs-telescope 0.1.10 → 0.1.12

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.
@@ -151,7 +151,7 @@ __decorate([
151
151
  __metadata("design:returntype", void 0)
152
152
  ], TelescopeController.prototype, "serveDashboard", null);
153
153
  __decorate([
154
- (0, common_1.Get)('*'),
154
+ (0, common_1.Get)('*path'),
155
155
  __param(0, (0, common_1.Req)()),
156
156
  __param(1, (0, common_1.Res)()),
157
157
  __metadata("design:type", Function),
@@ -78,7 +78,10 @@ let TelescopeRepository = class TelescopeRepository {
78
78
  return await this.entryRepo.findOne({ where: { uuid } });
79
79
  }
80
80
  async clearAll() {
81
- return await this.entryRepo.delete({});
81
+ return await this.entryRepo
82
+ .createQueryBuilder()
83
+ .delete()
84
+ .execute();
82
85
  }
83
86
  async countByType() {
84
87
  const results = await this.entryRepo
@@ -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,
@@ -93,19 +104,8 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
93
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
- if (item.isInterceptor) {
99
- providers.push({
100
- provide: core_1.APP_INTERCEPTOR,
101
- useClass: item.watcher,
102
- });
103
- }
104
- else {
105
- providers.push(item.watcher);
106
- }
107
- }
108
- }
107
+ // Register core watchers
108
+ this.registerWatchers(options, providers);
109
109
  // Optional dependencies checking:
110
110
  // 1. CacheWatcher (@nestjs/cache-manager)
111
111
  let hasCache = false;
@@ -181,6 +181,10 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
181
181
  // Dynamic Route Prefix Setup using Reflect metadata on TelescopeController
182
182
  const path = options.path || 'telescope';
183
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
+ }
184
188
  return {
185
189
  module: TelescopeModule_1,
186
190
  imports,
@@ -189,6 +193,44 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
189
193
  exports: [telescope_service_1.TelescopeService, telescope_repository_service_1.TelescopeRepository],
190
194
  };
191
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
+ }
192
234
  };
193
235
  exports.TelescopeModule = TelescopeModule;
194
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.10",
3
+ "version": "0.1.12",
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
  }