@ngn-net/nestjs-telescope 0.1.12 → 0.2.1
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 +29 -0
- package/dist/dashboard.provider.d.ts +5 -0
- package/dist/dashboard.provider.js +23 -0
- package/dist/enums/entry-type.enum.d.ts +1 -0
- package/dist/enums/entry-type.enum.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/interfaces/telescope-options.interface.d.ts +12 -0
- package/dist/telescope.module.d.ts +8 -4
- package/dist/telescope.module.js +201 -108
- package/dist/telescope.service.d.ts +7 -1
- package/dist/telescope.service.js +35 -2
- package/dist/ui/manifest.json +5 -0
- package/dist/watchers/http-client.watcher.d.ts +11 -0
- package/dist/watchers/http-client.watcher.js +200 -0
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @ngn-net/nestjs-telescope
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
A full-featured developer assistant and application monitoring suite for NestJS, inspired by Laravel Telescope. It monitors incoming HTTP requests, database queries, cache operations, background queue jobs, application events, outgoing mail, logs, unhandled exceptions, cron job schedules, and Redis commands.
|
|
4
6
|
|
|
5
7
|
All telemetry records are persisted to a database table and accessible via a modern, high-fidelity dark-themed single-page application dashboard.
|
|
@@ -93,6 +95,33 @@ import { TelescopeModule, EntryType } from '@ngn-net/nestjs-telescope';
|
|
|
93
95
|
export class AppModule {}
|
|
94
96
|
```
|
|
95
97
|
|
|
98
|
+
### Async Configuration
|
|
99
|
+
|
|
100
|
+
You can also register `TelescopeModule` asynchronously using a factory (e.g. inject `ConfigService`):
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { Module } from '@nestjs/common';
|
|
104
|
+
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
105
|
+
import { TelescopeModule } from '@ngn-net/nestjs-telescope';
|
|
106
|
+
|
|
107
|
+
@Module({
|
|
108
|
+
imports: [
|
|
109
|
+
ConfigModule.forRoot(),
|
|
110
|
+
TelescopeModule.forRootAsync({
|
|
111
|
+
imports: [ConfigModule],
|
|
112
|
+
inject: [ConfigService],
|
|
113
|
+
useFactory: (configService: ConfigService) => ({
|
|
114
|
+
path: configService.get('TELESCOPE_PATH', 'telescope'),
|
|
115
|
+
password: configService.get('TELESCOPE_PASSWORD', 'password'),
|
|
116
|
+
jwtSecret: configService.get('TELESCOPE_JWT_SECRET', 'my-secret'),
|
|
117
|
+
maxEntries: 1000,
|
|
118
|
+
}),
|
|
119
|
+
}),
|
|
120
|
+
],
|
|
121
|
+
})
|
|
122
|
+
export class AppModule {}
|
|
123
|
+
```
|
|
124
|
+
|
|
96
125
|
---
|
|
97
126
|
|
|
98
127
|
## Dashboard Access
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.DashboardStaticMiddleware = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
let DashboardStaticMiddleware = class DashboardStaticMiddleware {
|
|
12
|
+
use(req, res, next) {
|
|
13
|
+
// Let Nest's ServeStatic handle static files; fallback to index.html for SPA routing
|
|
14
|
+
if (req.baseUrl && req.path.startsWith(req.baseUrl)) {
|
|
15
|
+
return next();
|
|
16
|
+
}
|
|
17
|
+
next();
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
exports.DashboardStaticMiddleware = DashboardStaticMiddleware;
|
|
21
|
+
exports.DashboardStaticMiddleware = DashboardStaticMiddleware = __decorate([
|
|
22
|
+
(0, common_1.Injectable)()
|
|
23
|
+
], DashboardStaticMiddleware);
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './interfaces/telescope-options.interface';
|
|
|
7
7
|
export * from './interfaces/entry.interface';
|
|
8
8
|
export * from './guards/telescope-jwt.guard';
|
|
9
9
|
export * from './constants';
|
|
10
|
+
export * from './dashboard.provider';
|
|
10
11
|
export * from './watchers/http-request.watcher';
|
|
11
12
|
export * from './watchers/query.watcher';
|
|
12
13
|
export * from './watchers/cache.watcher';
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __exportStar(require("./interfaces/telescope-options.interface"), exports);
|
|
|
24
24
|
__exportStar(require("./interfaces/entry.interface"), exports);
|
|
25
25
|
__exportStar(require("./guards/telescope-jwt.guard"), exports);
|
|
26
26
|
__exportStar(require("./constants"), exports);
|
|
27
|
+
__exportStar(require("./dashboard.provider"), exports);
|
|
27
28
|
// Export all watchers for custom usage/extension
|
|
28
29
|
__exportStar(require("./watchers/http-request.watcher"), exports);
|
|
29
30
|
__exportStar(require("./watchers/query.watcher"), exports);
|
|
@@ -4,6 +4,8 @@ export interface TelescopeOptions {
|
|
|
4
4
|
path?: string;
|
|
5
5
|
/** JWT secret used for UI authentication */
|
|
6
6
|
jwtSecret?: string;
|
|
7
|
+
/** Enable the dashboard UI (default: true) */
|
|
8
|
+
enableDashboard?: boolean;
|
|
7
9
|
/** Password for dashboard login (default: 'password', or env TELESCOPE_PASSWORD) */
|
|
8
10
|
password?: string;
|
|
9
11
|
/** Which entry types to enable (default: all) */
|
|
@@ -17,3 +19,13 @@ export interface TelescopeOptions {
|
|
|
17
19
|
/** Additional middleware to apply to UI routes */
|
|
18
20
|
uiMiddleware?: any[];
|
|
19
21
|
}
|
|
22
|
+
export interface TelescopeOptionsFactory {
|
|
23
|
+
createTelescopeOptions(): Promise<TelescopeOptions> | TelescopeOptions;
|
|
24
|
+
}
|
|
25
|
+
export interface TelescopeModuleAsyncOptions {
|
|
26
|
+
imports?: any[];
|
|
27
|
+
useExisting?: any;
|
|
28
|
+
useClass?: any;
|
|
29
|
+
useFactory?: (...args: any[]) => Promise<TelescopeOptions> | TelescopeOptions;
|
|
30
|
+
inject?: any[];
|
|
31
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { DynamicModule, OnModuleInit } from '@nestjs/common';
|
|
2
2
|
import { ModuleRef } from '@nestjs/core';
|
|
3
|
+
import { TelescopeOptions, TelescopeModuleAsyncOptions } from './interfaces/telescope-options.interface';
|
|
3
4
|
export declare class TelescopeModule implements OnModuleInit {
|
|
4
5
|
private readonly moduleRef;
|
|
5
|
-
|
|
6
|
+
private readonly options;
|
|
7
|
+
constructor(moduleRef: ModuleRef, options: TelescopeOptions);
|
|
6
8
|
onModuleInit(): void;
|
|
7
|
-
static forRoot(options?:
|
|
8
|
-
|
|
9
|
-
static
|
|
9
|
+
static forRoot(options?: TelescopeOptions): DynamicModule;
|
|
10
|
+
static forRootAsync(options: TelescopeModuleAsyncOptions): DynamicModule;
|
|
11
|
+
private static createAsyncProviders;
|
|
12
|
+
private static registerWatchersAndModules;
|
|
13
|
+
private static registerWatchersAndModulesAsync;
|
|
10
14
|
}
|
package/dist/telescope.module.js
CHANGED
|
@@ -8,10 +8,12 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
11
14
|
var TelescopeModule_1;
|
|
12
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
16
|
exports.TelescopeModule = void 0;
|
|
14
|
-
// src/telescope.module.ts
|
|
15
17
|
const common_1 = require("@nestjs/common");
|
|
16
18
|
const serve_static_1 = require("@nestjs/serve-static");
|
|
17
19
|
const path_1 = require("path");
|
|
@@ -37,10 +39,37 @@ const constants_1 = require("./constants");
|
|
|
37
39
|
const entry_type_enum_1 = require("./enums/entry-type.enum");
|
|
38
40
|
let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
|
|
39
41
|
moduleRef;
|
|
40
|
-
|
|
42
|
+
options;
|
|
43
|
+
constructor(moduleRef, options) {
|
|
41
44
|
this.moduleRef = moduleRef;
|
|
45
|
+
this.options = options;
|
|
46
|
+
const path = this.options.path || constants_1.DEFAULT_TELESCOPE_PATH;
|
|
47
|
+
Reflect.defineMetadata('path', path, telescope_controller_1.TelescopeController);
|
|
42
48
|
}
|
|
43
49
|
onModuleInit() {
|
|
50
|
+
// Dynamic peer dependency validation at bootstrap
|
|
51
|
+
const warnedTypes = new Set();
|
|
52
|
+
const hasDependency = (type, packageName) => {
|
|
53
|
+
try {
|
|
54
|
+
require(packageName);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
if (this.options.enabledEntryTypes && this.options.enabledEntryTypes.includes(type)) {
|
|
59
|
+
if (!warnedTypes.has(type)) {
|
|
60
|
+
warnedTypes.add(type);
|
|
61
|
+
common_1.Logger.warn(`EntryType '${type}' was explicitly enabled in config, but the required peer dependency '${packageName}' is not installed. The '${type}' watcher has been disabled.`, 'TelescopeModule');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
hasDependency(entry_type_enum_1.EntryType.CACHE, '@nestjs/cache-manager');
|
|
68
|
+
hasDependency(entry_type_enum_1.EntryType.JOB, '@nestjs/bullmq');
|
|
69
|
+
hasDependency(entry_type_enum_1.EntryType.JOB, 'bullmq');
|
|
70
|
+
hasDependency(entry_type_enum_1.EntryType.EVENT, '@nestjs/event-emitter');
|
|
71
|
+
hasDependency(entry_type_enum_1.EntryType.SCHEDULED_TASK, '@nestjs/schedule');
|
|
72
|
+
hasDependency(entry_type_enum_1.EntryType.MAIL, 'nodemailer');
|
|
44
73
|
// Dynamic EventEmitter2 configuration
|
|
45
74
|
try {
|
|
46
75
|
const eventWatcher = this.moduleRef.get(event_watcher_1.EventWatcher, { strict: false });
|
|
@@ -81,7 +110,7 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
|
|
|
81
110
|
// Serve static UI assets if dashboard is enabled (default true)
|
|
82
111
|
if (options.enableDashboard !== false) {
|
|
83
112
|
const staticPath = (0, path_1.join)(__dirname, '..', 'ui');
|
|
84
|
-
const routePath = options.path ||
|
|
113
|
+
const routePath = options.path || constants_1.DEFAULT_TELESCOPE_PATH;
|
|
85
114
|
imports.push(serve_static_1.ServeStaticModule.forRoot({
|
|
86
115
|
rootPath: staticPath,
|
|
87
116
|
serveRoot: `/${routePath}`,
|
|
@@ -96,145 +125,209 @@ let TelescopeModule = TelescopeModule_1 = class TelescopeModule {
|
|
|
96
125
|
telescope_repository_service_1.TelescopeRepository,
|
|
97
126
|
telescope_jwt_guard_1.JwtAuthGuard,
|
|
98
127
|
];
|
|
99
|
-
//
|
|
100
|
-
|
|
128
|
+
// Register watchers (both core and optional)
|
|
129
|
+
this.registerWatchersAndModules(options, providers, imports);
|
|
130
|
+
return {
|
|
131
|
+
module: TelescopeModule_1,
|
|
132
|
+
imports,
|
|
133
|
+
providers,
|
|
134
|
+
controllers: [telescope_controller_1.TelescopeController],
|
|
135
|
+
exports: [telescope_service_1.TelescopeService, telescope_repository_service_1.TelescopeRepository],
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
static forRootAsync(options) {
|
|
139
|
+
const providers = [
|
|
140
|
+
...this.createAsyncProviders(options),
|
|
141
|
+
telescope_service_1.TelescopeService,
|
|
142
|
+
telescope_repository_service_1.TelescopeRepository,
|
|
143
|
+
telescope_jwt_guard_1.JwtAuthGuard,
|
|
144
|
+
];
|
|
145
|
+
const imports = [
|
|
146
|
+
typeorm_1.TypeOrmModule.forFeature([telescope_entry_entity_1.TelescopeEntry]),
|
|
147
|
+
jwt_1.JwtModule.registerAsync({
|
|
148
|
+
imports: options.imports || [],
|
|
149
|
+
inject: [constants_1.TELESCOPE_OPTIONS],
|
|
150
|
+
useFactory: async (telescopeOpts) => ({
|
|
151
|
+
secret: telescopeOpts.jwtSecret || process.env.TELESCOPE_JWT_SECRET || constants_1.DEFAULT_JWT_SECRET,
|
|
152
|
+
signOptions: { expiresIn: '1d' },
|
|
153
|
+
}),
|
|
154
|
+
}),
|
|
155
|
+
];
|
|
156
|
+
// Serve static UI assets if dashboard is enabled (default true)
|
|
157
|
+
imports.push(serve_static_1.ServeStaticModule.forRootAsync({
|
|
158
|
+
imports: options.imports || [],
|
|
159
|
+
inject: [constants_1.TELESCOPE_OPTIONS],
|
|
160
|
+
useFactory: (telescopeOpts) => {
|
|
161
|
+
if (telescopeOpts.enableDashboard === false) {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
const staticPath = (0, path_1.join)(__dirname, '..', 'ui');
|
|
165
|
+
const routePath = telescopeOpts.path || constants_1.DEFAULT_TELESCOPE_PATH;
|
|
166
|
+
return [
|
|
167
|
+
{
|
|
168
|
+
rootPath: staticPath,
|
|
169
|
+
serveRoot: `/${routePath}`,
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
},
|
|
173
|
+
}));
|
|
174
|
+
// Register all available/supported watchers & modules dynamically
|
|
175
|
+
this.registerWatchersAndModulesAsync(providers, imports);
|
|
176
|
+
return {
|
|
177
|
+
module: TelescopeModule_1,
|
|
178
|
+
imports,
|
|
179
|
+
providers,
|
|
180
|
+
controllers: [telescope_controller_1.TelescopeController],
|
|
181
|
+
exports: [telescope_service_1.TelescopeService, telescope_repository_service_1.TelescopeRepository],
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
static createAsyncProviders(options) {
|
|
185
|
+
if (options.useFactory) {
|
|
186
|
+
return [
|
|
187
|
+
{
|
|
188
|
+
provide: constants_1.TELESCOPE_OPTIONS,
|
|
189
|
+
useFactory: options.useFactory,
|
|
190
|
+
inject: options.inject || [],
|
|
191
|
+
},
|
|
192
|
+
];
|
|
193
|
+
}
|
|
194
|
+
const useClass = options.useClass || options.useExisting;
|
|
195
|
+
if (useClass) {
|
|
196
|
+
return [
|
|
197
|
+
{
|
|
198
|
+
provide: constants_1.TELESCOPE_OPTIONS,
|
|
199
|
+
useFactory: async (optionsFactory) => await optionsFactory.createTelescopeOptions(),
|
|
200
|
+
inject: [useClass],
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
provide: useClass,
|
|
204
|
+
useClass: options.useClass,
|
|
205
|
+
},
|
|
206
|
+
];
|
|
207
|
+
}
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
static registerWatchersAndModules(options, providers, imports) {
|
|
211
|
+
// 1. Core watchers (always registered if enabled or not specified)
|
|
212
|
+
const coreWatchers = [
|
|
101
213
|
{ type: entry_type_enum_1.EntryType.REQUEST, watcher: http_request_watcher_1.HttpRequestWatcher, isInterceptor: true },
|
|
102
214
|
{ type: entry_type_enum_1.EntryType.QUERY, watcher: query_watcher_1.QueryWatcher },
|
|
103
215
|
{ type: entry_type_enum_1.EntryType.LOG, watcher: log_watcher_1.LogWatcher },
|
|
104
216
|
{ type: entry_type_enum_1.EntryType.EXCEPTION, watcher: exception_watcher_1.ExceptionWatcher, isInterceptor: true },
|
|
105
217
|
{ type: entry_type_enum_1.EntryType.REDIS, watcher: redis_watcher_1.RedisWatcher },
|
|
106
218
|
];
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
219
|
+
for (const item of coreWatchers) {
|
|
220
|
+
if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(item.type)) {
|
|
221
|
+
if (item.isInterceptor) {
|
|
222
|
+
providers.push({ provide: core_1.APP_INTERCEPTOR, useClass: item.watcher });
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
providers.push(item.watcher);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// 2. CacheWatcher
|
|
112
230
|
try {
|
|
113
231
|
require('@nestjs/cache-manager');
|
|
114
|
-
|
|
115
|
-
}
|
|
116
|
-
catch { }
|
|
117
|
-
if (hasCache && (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.CACHE))) {
|
|
118
|
-
try {
|
|
232
|
+
if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.CACHE)) {
|
|
119
233
|
const { CacheModule } = require('@nestjs/cache-manager');
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
providers.push(cache_watcher_1.CacheWatcher);
|
|
123
|
-
}
|
|
234
|
+
imports.push(CacheModule.register({}));
|
|
235
|
+
providers.push(cache_watcher_1.CacheWatcher);
|
|
124
236
|
}
|
|
125
|
-
catch { }
|
|
126
237
|
}
|
|
127
|
-
|
|
128
|
-
|
|
238
|
+
catch (e) { }
|
|
239
|
+
// 3. QueueWatcher
|
|
129
240
|
try {
|
|
130
241
|
require('@nestjs/bullmq');
|
|
131
242
|
require('bullmq');
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
catch { }
|
|
135
|
-
if (hasBull && (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.JOB))) {
|
|
136
|
-
try {
|
|
243
|
+
if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.JOB)) {
|
|
137
244
|
const { BullModule } = require('@nestjs/bullmq');
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
providers.push(queue_watcher_1.QueueWatcher);
|
|
147
|
-
}
|
|
245
|
+
imports.push(BullModule.registerQueue({
|
|
246
|
+
name: 'telescope-queue',
|
|
247
|
+
connection: {
|
|
248
|
+
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
249
|
+
port: Number(process.env.REDIS_PORT) || 6379,
|
|
250
|
+
},
|
|
251
|
+
}));
|
|
252
|
+
providers.push(queue_watcher_1.QueueWatcher);
|
|
148
253
|
}
|
|
149
|
-
catch { }
|
|
150
254
|
}
|
|
151
|
-
|
|
152
|
-
|
|
255
|
+
catch (e) { }
|
|
256
|
+
// 4. EventWatcher
|
|
153
257
|
try {
|
|
154
258
|
require('@nestjs/event-emitter');
|
|
155
|
-
|
|
259
|
+
if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.EVENT)) {
|
|
260
|
+
providers.push(event_watcher_1.EventWatcher);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (e) { }
|
|
264
|
+
// 5. ScheduleWatcher
|
|
265
|
+
try {
|
|
266
|
+
require('@nestjs/schedule');
|
|
267
|
+
if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.SCHEDULED_TASK)) {
|
|
268
|
+
providers.push(schedule_watcher_1.ScheduleWatcher);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
catch (e) { }
|
|
272
|
+
// 6. MailWatcher
|
|
273
|
+
try {
|
|
274
|
+
require('nodemailer');
|
|
275
|
+
if (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.MAIL)) {
|
|
276
|
+
providers.push(mail_watcher_1.MailWatcher);
|
|
277
|
+
}
|
|
156
278
|
}
|
|
157
|
-
catch { }
|
|
158
|
-
|
|
279
|
+
catch (e) { }
|
|
280
|
+
}
|
|
281
|
+
static registerWatchersAndModulesAsync(providers, imports) {
|
|
282
|
+
// Core interceptors and watchers
|
|
283
|
+
providers.push({ provide: core_1.APP_INTERCEPTOR, useClass: http_request_watcher_1.HttpRequestWatcher });
|
|
284
|
+
providers.push({ provide: core_1.APP_INTERCEPTOR, useClass: exception_watcher_1.ExceptionWatcher });
|
|
285
|
+
providers.push(query_watcher_1.QueryWatcher);
|
|
286
|
+
providers.push(log_watcher_1.LogWatcher);
|
|
287
|
+
providers.push(redis_watcher_1.RedisWatcher);
|
|
288
|
+
// Optional modules/watchers if packages are present
|
|
289
|
+
try {
|
|
290
|
+
require('@nestjs/cache-manager');
|
|
291
|
+
const { CacheModule } = require('@nestjs/cache-manager');
|
|
292
|
+
imports.push(CacheModule.register({}));
|
|
293
|
+
providers.push(cache_watcher_1.CacheWatcher);
|
|
294
|
+
}
|
|
295
|
+
catch (e) { }
|
|
296
|
+
try {
|
|
297
|
+
require('@nestjs/bullmq');
|
|
298
|
+
require('bullmq');
|
|
299
|
+
const { BullModule } = require('@nestjs/bullmq');
|
|
300
|
+
imports.push(BullModule.registerQueue({
|
|
301
|
+
name: 'telescope-queue',
|
|
302
|
+
connection: {
|
|
303
|
+
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
304
|
+
port: Number(process.env.REDIS_PORT) || 6379,
|
|
305
|
+
},
|
|
306
|
+
}));
|
|
307
|
+
providers.push(queue_watcher_1.QueueWatcher);
|
|
308
|
+
}
|
|
309
|
+
catch (e) { }
|
|
310
|
+
try {
|
|
311
|
+
require('@nestjs/event-emitter');
|
|
159
312
|
providers.push(event_watcher_1.EventWatcher);
|
|
160
313
|
}
|
|
161
|
-
|
|
162
|
-
let hasSchedule = false;
|
|
314
|
+
catch (e) { }
|
|
163
315
|
try {
|
|
164
316
|
require('@nestjs/schedule');
|
|
165
|
-
hasSchedule = true;
|
|
166
|
-
}
|
|
167
|
-
catch { }
|
|
168
|
-
if (hasSchedule && (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.SCHEDULED_TASK))) {
|
|
169
317
|
providers.push(schedule_watcher_1.ScheduleWatcher);
|
|
170
318
|
}
|
|
171
|
-
|
|
172
|
-
let hasNodemailer = false;
|
|
319
|
+
catch (e) { }
|
|
173
320
|
try {
|
|
174
321
|
require('nodemailer');
|
|
175
|
-
hasNodemailer = true;
|
|
176
|
-
}
|
|
177
|
-
catch { }
|
|
178
|
-
if (hasNodemailer && (!options.enabledEntryTypes || options.enabledEntryTypes.includes(entry_type_enum_1.EntryType.MAIL))) {
|
|
179
322
|
providers.push(mail_watcher_1.MailWatcher);
|
|
180
323
|
}
|
|
181
|
-
|
|
182
|
-
const path = options.path || 'telescope';
|
|
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
|
-
}
|
|
188
|
-
return {
|
|
189
|
-
module: TelescopeModule_1,
|
|
190
|
-
imports,
|
|
191
|
-
providers,
|
|
192
|
-
controllers: [telescope_controller_1.TelescopeController],
|
|
193
|
-
exports: [telescope_service_1.TelescopeService, telescope_repository_service_1.TelescopeRepository],
|
|
194
|
-
};
|
|
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
|
-
};
|
|
324
|
+
catch (e) { }
|
|
233
325
|
}
|
|
234
326
|
};
|
|
235
327
|
exports.TelescopeModule = TelescopeModule;
|
|
236
328
|
exports.TelescopeModule = TelescopeModule = TelescopeModule_1 = __decorate([
|
|
237
329
|
(0, common_1.Global)(),
|
|
238
330
|
(0, common_1.Module)({}),
|
|
239
|
-
|
|
331
|
+
__param(1, (0, common_1.Inject)(constants_1.TELESCOPE_OPTIONS)),
|
|
332
|
+
__metadata("design:paramtypes", [core_1.ModuleRef, Object])
|
|
240
333
|
], TelescopeModule);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { HttpAdapterHost } from '@nestjs/core';
|
|
2
3
|
import { TelescopeRepository } from './storage/telescope-repository.service';
|
|
3
4
|
import { TelescopeEntry } from './storage/entities/telescope-entry.entity';
|
|
4
5
|
import { EntryType } from './enums/entry-type.enum';
|
|
@@ -6,10 +7,15 @@ import { TelescopeOptions } from './interfaces/telescope-options.interface';
|
|
|
6
7
|
export declare class TelescopeService implements OnModuleInit {
|
|
7
8
|
private readonly repo;
|
|
8
9
|
private readonly options?;
|
|
10
|
+
private readonly adapterHost?;
|
|
9
11
|
private lastPruneTime;
|
|
10
12
|
private readonly asyncLocalStorage;
|
|
11
|
-
constructor(repo: TelescopeRepository, options?: TelescopeOptions | undefined);
|
|
13
|
+
constructor(repo: TelescopeRepository, options?: TelescopeOptions | undefined, adapterHost?: HttpAdapterHost | undefined);
|
|
12
14
|
onModuleInit(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Get the full URL to the Telescope dashboard.
|
|
17
|
+
*/
|
|
18
|
+
getDashboardUrl(): string;
|
|
13
19
|
/**
|
|
14
20
|
* Check if a specific entry type is enabled in the options.
|
|
15
21
|
* If `enabledEntryTypes` is not configured, all types are enabled.
|
|
@@ -16,20 +16,52 @@ exports.TelescopeService = void 0;
|
|
|
16
16
|
// src/telescope.service.ts
|
|
17
17
|
const common_1 = require("@nestjs/common");
|
|
18
18
|
const async_hooks_1 = require("async_hooks");
|
|
19
|
+
const core_1 = require("@nestjs/core");
|
|
19
20
|
const telescope_repository_service_1 = require("./storage/telescope-repository.service");
|
|
20
21
|
const constants_1 = require("./constants");
|
|
21
22
|
let TelescopeService = class TelescopeService {
|
|
22
23
|
repo;
|
|
23
24
|
options;
|
|
25
|
+
adapterHost;
|
|
24
26
|
lastPruneTime = 0;
|
|
25
27
|
asyncLocalStorage = new async_hooks_1.AsyncLocalStorage();
|
|
26
|
-
constructor(repo, options) {
|
|
28
|
+
constructor(repo, options, adapterHost) {
|
|
27
29
|
this.repo = repo;
|
|
28
30
|
this.options = options;
|
|
31
|
+
this.adapterHost = adapterHost;
|
|
29
32
|
}
|
|
30
33
|
async onModuleInit() {
|
|
31
34
|
// Module is initialized; TypeORM will handle schema sync
|
|
32
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the full URL to the Telescope dashboard.
|
|
38
|
+
*/
|
|
39
|
+
getDashboardUrl() {
|
|
40
|
+
const path = this.options?.path || 'telescope';
|
|
41
|
+
if (!this.adapterHost) {
|
|
42
|
+
return `/${path}`;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const httpAdapter = this.adapterHost.httpAdapter;
|
|
46
|
+
if (!httpAdapter) {
|
|
47
|
+
return `/${path}`;
|
|
48
|
+
}
|
|
49
|
+
const instance = httpAdapter.getInstance();
|
|
50
|
+
if (instance && typeof instance.address === 'function') {
|
|
51
|
+
const address = instance.address();
|
|
52
|
+
if (address) {
|
|
53
|
+
const host = typeof address === 'string' ? address : address.address === '::' ? 'localhost' : address.address;
|
|
54
|
+
const port = typeof address === 'string' ? '' : `:${address.port}`;
|
|
55
|
+
const protocol = 'http';
|
|
56
|
+
return `${protocol}://${host}${port}/${path}`;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
// Fallback
|
|
62
|
+
}
|
|
63
|
+
return `/${path}`;
|
|
64
|
+
}
|
|
33
65
|
/**
|
|
34
66
|
* Check if a specific entry type is enabled in the options.
|
|
35
67
|
* If `enabledEntryTypes` is not configured, all types are enabled.
|
|
@@ -110,5 +142,6 @@ exports.TelescopeService = TelescopeService = __decorate([
|
|
|
110
142
|
(0, common_1.Injectable)(),
|
|
111
143
|
__param(1, (0, common_1.Inject)(constants_1.TELESCOPE_OPTIONS)),
|
|
112
144
|
__param(1, (0, common_1.Optional)()),
|
|
113
|
-
|
|
145
|
+
__param(2, (0, common_1.Optional)()),
|
|
146
|
+
__metadata("design:paramtypes", [telescope_repository_service_1.TelescopeRepository, Object, core_1.HttpAdapterHost])
|
|
114
147
|
], TelescopeService);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { OnModuleInit } from '@nestjs/common';
|
|
2
|
+
import { TelescopeService } from '../telescope.service';
|
|
3
|
+
import { TelescopeOptions } from '../interfaces/telescope-options.interface';
|
|
4
|
+
export declare class HttpClientWatcher implements OnModuleInit {
|
|
5
|
+
private readonly telescope;
|
|
6
|
+
private readonly options?;
|
|
7
|
+
private patched;
|
|
8
|
+
constructor(telescope: TelescopeService, options?: TelescopeOptions | undefined);
|
|
9
|
+
onModuleInit(): void;
|
|
10
|
+
private patch;
|
|
11
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
19
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
20
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
21
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
22
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
23
|
+
};
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
42
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
43
|
+
};
|
|
44
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
45
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.HttpClientWatcher = void 0;
|
|
49
|
+
// src/watchers/http-client.watcher.ts
|
|
50
|
+
const common_1 = require("@nestjs/common");
|
|
51
|
+
const http = __importStar(require("http"));
|
|
52
|
+
const https = __importStar(require("https"));
|
|
53
|
+
const telescope_service_1 = require("../telescope.service");
|
|
54
|
+
const entry_type_enum_1 = require("../enums/entry-type.enum");
|
|
55
|
+
const constants_1 = require("../constants");
|
|
56
|
+
let HttpClientWatcher = class HttpClientWatcher {
|
|
57
|
+
telescope;
|
|
58
|
+
options;
|
|
59
|
+
patched = false;
|
|
60
|
+
constructor(telescope, options) {
|
|
61
|
+
this.telescope = telescope;
|
|
62
|
+
this.options = options;
|
|
63
|
+
}
|
|
64
|
+
onModuleInit() {
|
|
65
|
+
if (this.patched)
|
|
66
|
+
return;
|
|
67
|
+
this.patched = true;
|
|
68
|
+
this.patch(http);
|
|
69
|
+
this.patch(https);
|
|
70
|
+
}
|
|
71
|
+
patch(module) {
|
|
72
|
+
const originalRequest = module.request.bind(module);
|
|
73
|
+
const self = this;
|
|
74
|
+
// Override module.request
|
|
75
|
+
module.request = function (...args) {
|
|
76
|
+
const req = originalRequest(...args);
|
|
77
|
+
const startTime = Date.now();
|
|
78
|
+
// Resolve URL and method
|
|
79
|
+
let urlStr = '';
|
|
80
|
+
let method = 'GET';
|
|
81
|
+
try {
|
|
82
|
+
const firstArg = args[0];
|
|
83
|
+
if (typeof firstArg === 'string') {
|
|
84
|
+
urlStr = firstArg;
|
|
85
|
+
}
|
|
86
|
+
else if (firstArg instanceof URL) {
|
|
87
|
+
urlStr = firstArg.toString();
|
|
88
|
+
}
|
|
89
|
+
else if (typeof firstArg === 'object' && firstArg !== null) {
|
|
90
|
+
const { protocol, hostname, host, port, path: urlPath } = firstArg;
|
|
91
|
+
const proto = protocol || 'http:';
|
|
92
|
+
const h = hostname || host || 'localhost';
|
|
93
|
+
const p = port ? `:${port}` : '';
|
|
94
|
+
urlStr = `${proto}//${h}${p}${urlPath || '/'}`;
|
|
95
|
+
method = (firstArg.method || 'GET').toUpperCase();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
urlStr = '';
|
|
100
|
+
}
|
|
101
|
+
// Ignore telescope's own internal paths and configured ignore list
|
|
102
|
+
if (self.telescope.shouldIgnorePath(urlStr)) {
|
|
103
|
+
return req;
|
|
104
|
+
}
|
|
105
|
+
// Skip if this type is disabled
|
|
106
|
+
if (!self.telescope.isEnabled(entry_type_enum_1.EntryType.HTTP_CLIENT)) {
|
|
107
|
+
return req;
|
|
108
|
+
}
|
|
109
|
+
// Capture request body chunks written
|
|
110
|
+
const reqChunks = [];
|
|
111
|
+
const originalWrite = req.write.bind(req);
|
|
112
|
+
const originalEnd = req.end.bind(req);
|
|
113
|
+
req.write = function (chunk, ...rest) {
|
|
114
|
+
if (chunk)
|
|
115
|
+
reqChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
116
|
+
return originalWrite(chunk, ...rest);
|
|
117
|
+
};
|
|
118
|
+
req.end = function (chunk, ...rest) {
|
|
119
|
+
if (chunk)
|
|
120
|
+
reqChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
121
|
+
return originalEnd(chunk, ...rest);
|
|
122
|
+
};
|
|
123
|
+
// Capture request headers snapshot after end
|
|
124
|
+
req.on('finish', () => {
|
|
125
|
+
// headers captured at response time
|
|
126
|
+
});
|
|
127
|
+
req.on('response', (res) => {
|
|
128
|
+
const resChunks = [];
|
|
129
|
+
res.on('data', (chunk) => {
|
|
130
|
+
resChunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
131
|
+
});
|
|
132
|
+
res.on('end', () => {
|
|
133
|
+
const duration = Date.now() - startTime;
|
|
134
|
+
const requestBodyRaw = Buffer.concat(reqChunks).toString('utf8');
|
|
135
|
+
const responseBodyRaw = Buffer.concat(resChunks).toString('utf8');
|
|
136
|
+
let requestBody = requestBodyRaw;
|
|
137
|
+
let responseBody = responseBodyRaw;
|
|
138
|
+
try {
|
|
139
|
+
requestBody = JSON.parse(requestBodyRaw);
|
|
140
|
+
}
|
|
141
|
+
catch { /* keep raw */ }
|
|
142
|
+
try {
|
|
143
|
+
responseBody = JSON.parse(responseBodyRaw);
|
|
144
|
+
}
|
|
145
|
+
catch { /* keep raw */ }
|
|
146
|
+
// Sanitize headers — remove Authorization tokens from logs
|
|
147
|
+
const reqHeaders = { ...req.getHeaders?.() };
|
|
148
|
+
if (reqHeaders['authorization']) {
|
|
149
|
+
reqHeaders['authorization'] = '[REDACTED]';
|
|
150
|
+
}
|
|
151
|
+
self.telescope.record({
|
|
152
|
+
type: entry_type_enum_1.EntryType.HTTP_CLIENT,
|
|
153
|
+
content: {
|
|
154
|
+
method: method || (req.method || 'GET').toUpperCase(),
|
|
155
|
+
url: urlStr,
|
|
156
|
+
requestHeaders: reqHeaders,
|
|
157
|
+
requestBody,
|
|
158
|
+
responseStatus: res.statusCode,
|
|
159
|
+
responseHeaders: res.headers,
|
|
160
|
+
responseBody,
|
|
161
|
+
duration,
|
|
162
|
+
},
|
|
163
|
+
}).catch(() => { });
|
|
164
|
+
});
|
|
165
|
+
res.on('error', () => { });
|
|
166
|
+
});
|
|
167
|
+
req.on('error', (err) => {
|
|
168
|
+
const duration = Date.now() - startTime;
|
|
169
|
+
self.telescope.record({
|
|
170
|
+
type: entry_type_enum_1.EntryType.HTTP_CLIENT,
|
|
171
|
+
content: {
|
|
172
|
+
method,
|
|
173
|
+
url: urlStr,
|
|
174
|
+
requestHeaders: {},
|
|
175
|
+
requestBody: null,
|
|
176
|
+
responseStatus: 0,
|
|
177
|
+
responseBody: null,
|
|
178
|
+
error: err.message,
|
|
179
|
+
duration,
|
|
180
|
+
},
|
|
181
|
+
}).catch(() => { });
|
|
182
|
+
});
|
|
183
|
+
return req;
|
|
184
|
+
};
|
|
185
|
+
// Also handle module.get (convenience method)
|
|
186
|
+
const originalGet = module.get.bind(module);
|
|
187
|
+
module.get = function (...args) {
|
|
188
|
+
const req = module.request(...args);
|
|
189
|
+
req.end();
|
|
190
|
+
return req;
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
exports.HttpClientWatcher = HttpClientWatcher;
|
|
195
|
+
exports.HttpClientWatcher = HttpClientWatcher = __decorate([
|
|
196
|
+
(0, common_1.Injectable)(),
|
|
197
|
+
__param(1, (0, common_1.Inject)(constants_1.TELESCOPE_OPTIONS)),
|
|
198
|
+
__param(1, (0, common_1.Optional)()),
|
|
199
|
+
__metadata("design:paramtypes", [telescope_service_1.TelescopeService, Object])
|
|
200
|
+
], HttpClientWatcher);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ngn-net/nestjs-telescope",
|
|
3
|
-
"version": "0.1
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -73,16 +73,21 @@
|
|
|
73
73
|
"@nestjs/event-emitter": "^2",
|
|
74
74
|
"@nestjs/jwt": "^11",
|
|
75
75
|
"@nestjs/passport": "^11",
|
|
76
|
+
"@nestjs/platform-express": "^11.1.25",
|
|
76
77
|
"@nestjs/schedule": "^4",
|
|
78
|
+
"@nestjs/testing": "^11.1.25",
|
|
77
79
|
"@nestjs/typeorm": "^11",
|
|
78
80
|
"@types/express": "^4.17.21",
|
|
79
81
|
"@types/jest": "^29",
|
|
80
82
|
"@types/node": "^20",
|
|
81
83
|
"@types/nodemailer": "^6.4.15",
|
|
84
|
+
"@types/supertest": "^7.2.0",
|
|
82
85
|
"jest": "^29",
|
|
83
86
|
"nodemailer": "^6",
|
|
84
87
|
"rimraf": "^5",
|
|
85
88
|
"rxjs": "^7",
|
|
89
|
+
"sqlite3": "^6.0.1",
|
|
90
|
+
"supertest": "^7.2.2",
|
|
86
91
|
"ts-jest": "^29",
|
|
87
92
|
"ts-node": "^10",
|
|
88
93
|
"typeorm": "^0.3",
|