@geekmidas/telescope 0.0.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 +521 -0
- package/dist/Telescope-B3Wd82yk.cjs +602 -0
- package/dist/Telescope-B3Wd82yk.cjs.map +1 -0
- package/dist/Telescope-C5dyDYYB.d.cts +133 -0
- package/dist/Telescope-D-uoZB6b.mjs +596 -0
- package/dist/Telescope-D-uoZB6b.mjs.map +1 -0
- package/dist/Telescope-DyIWgh9-.d.mts +133 -0
- package/dist/Telescope.cjs +3 -0
- package/dist/Telescope.d.cts +3 -0
- package/dist/Telescope.d.mts +3 -0
- package/dist/Telescope.mjs +3 -0
- package/dist/chunk-CUT6urMc.cjs +30 -0
- package/dist/index.cjs +5 -0
- package/dist/index.d.cts +4 -0
- package/dist/index.d.mts +4 -0
- package/dist/index.mjs +4 -0
- package/dist/logger/console.cjs +161 -0
- package/dist/logger/console.cjs.map +1 -0
- package/dist/logger/console.d.cts +109 -0
- package/dist/logger/console.d.mts +109 -0
- package/dist/logger/console.mjs +159 -0
- package/dist/logger/console.mjs.map +1 -0
- package/dist/logger/pino.cjs +118 -0
- package/dist/logger/pino.cjs.map +1 -0
- package/dist/logger/pino.d.cts +89 -0
- package/dist/logger/pino.d.mts +89 -0
- package/dist/logger/pino.mjs +116 -0
- package/dist/logger/pino.mjs.map +1 -0
- package/dist/memory-9-B9WACq.cjs +110 -0
- package/dist/memory-9-B9WACq.cjs.map +1 -0
- package/dist/memory-Cm0eevCS.d.mts +38 -0
- package/dist/memory-DiP1a-pp.d.cts +38 -0
- package/dist/memory-SdN5vtG9.mjs +104 -0
- package/dist/memory-SdN5vtG9.mjs.map +1 -0
- package/dist/server/hono.cjs +180 -0
- package/dist/server/hono.cjs.map +1 -0
- package/dist/server/hono.d.cts +26 -0
- package/dist/server/hono.d.mts +26 -0
- package/dist/server/hono.mjs +176 -0
- package/dist/server/hono.mjs.map +1 -0
- package/dist/storage/kysely.cjs +336 -0
- package/dist/storage/kysely.cjs.map +1 -0
- package/dist/storage/kysely.d.cts +161 -0
- package/dist/storage/kysely.d.mts +161 -0
- package/dist/storage/kysely.mjs +334 -0
- package/dist/storage/kysely.mjs.map +1 -0
- package/dist/storage/memory.cjs +3 -0
- package/dist/storage/memory.d.cts +3 -0
- package/dist/storage/memory.d.mts +3 -0
- package/dist/storage/memory.mjs +3 -0
- package/dist/types-BGDhFv4R.d.cts +170 -0
- package/dist/types-CZbzz8kx.d.mts +170 -0
- package/dist/types.cjs +0 -0
- package/dist/types.d.cts +2 -0
- package/dist/types.d.mts +2 -0
- package/dist/types.mjs +0 -0
- package/dist/ui-assets-D6-8TAr_.mjs +30 -0
- package/dist/ui-assets-D6-8TAr_.mjs.map +1 -0
- package/dist/ui-assets-ulevVble.cjs +48 -0
- package/dist/ui-assets-ulevVble.cjs.map +1 -0
- package/dist/ui-assets.cjs +5 -0
- package/dist/ui-assets.d.cts +12 -0
- package/dist/ui-assets.d.mts +12 -0
- package/dist/ui-assets.mjs +3 -0
- package/package.json +83 -0
- package/scripts/embed-ui.ts +90 -0
- package/src/Telescope.ts +714 -0
- package/src/__tests__/Telescope.spec.ts +356 -0
- package/src/index.ts +23 -0
- package/src/logger/__tests__/console.spec.ts +266 -0
- package/src/logger/__tests__/pino.spec.ts +217 -0
- package/src/logger/console.ts +230 -0
- package/src/logger/pino.ts +191 -0
- package/src/server/__tests__/hono.spec.ts +340 -0
- package/src/server/hono.ts +247 -0
- package/src/storage/__tests__/kysely.spec.ts +715 -0
- package/src/storage/__tests__/memory.spec.ts +411 -0
- package/src/storage/kysely.ts +572 -0
- package/src/storage/memory.ts +168 -0
- package/src/types.ts +188 -0
- package/src/ui-assets.ts +40 -0
- package/ui/index.html +12 -0
- package/ui/node_modules/.bin/browserslist +21 -0
- package/ui/node_modules/.bin/jiti +21 -0
- package/ui/node_modules/.bin/terser +21 -0
- package/ui/node_modules/.bin/tsc +21 -0
- package/ui/node_modules/.bin/tsserver +21 -0
- package/ui/node_modules/.bin/tsx +21 -0
- package/ui/node_modules/.bin/vite +21 -0
- package/ui/package.json +24 -0
- package/ui/src/App.tsx +342 -0
- package/ui/src/api.ts +75 -0
- package/ui/src/components/ExceptionDetail.tsx +100 -0
- package/ui/src/components/LogDetail.tsx +91 -0
- package/ui/src/components/RequestDetail.tsx +143 -0
- package/ui/src/main.tsx +10 -0
- package/ui/src/styles.css +10 -0
- package/ui/src/types.ts +63 -0
- package/ui/src/vite-env.d.ts +1 -0
- package/ui/src/vite-plugin-gkm-config.ts +54 -0
- package/ui/tsconfig.json +20 -0
- package/ui/tsconfig.tsbuildinfo +14 -0
- package/ui/vite.config.ts +13 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/storage/memory.ts
|
|
3
|
+
/**
|
|
4
|
+
* In-memory storage for Telescope data.
|
|
5
|
+
* Ideal for development and testing.
|
|
6
|
+
* Data is lost when the process restarts.
|
|
7
|
+
*/
|
|
8
|
+
var InMemoryStorage = class {
|
|
9
|
+
requests = [];
|
|
10
|
+
exceptions = [];
|
|
11
|
+
logs = [];
|
|
12
|
+
maxEntries;
|
|
13
|
+
constructor(options) {
|
|
14
|
+
this.maxEntries = options?.maxEntries ?? 1e3;
|
|
15
|
+
}
|
|
16
|
+
async saveRequest(entry) {
|
|
17
|
+
this.requests.unshift(entry);
|
|
18
|
+
this.enforceLimit("requests");
|
|
19
|
+
}
|
|
20
|
+
async saveRequests(entries) {
|
|
21
|
+
this.requests.unshift(...entries);
|
|
22
|
+
this.enforceLimit("requests");
|
|
23
|
+
}
|
|
24
|
+
async getRequests(options) {
|
|
25
|
+
return this.filterEntries(this.requests, options);
|
|
26
|
+
}
|
|
27
|
+
async getRequest(id) {
|
|
28
|
+
return this.requests.find((r) => r.id === id) ?? null;
|
|
29
|
+
}
|
|
30
|
+
async saveException(entry) {
|
|
31
|
+
this.exceptions.unshift(entry);
|
|
32
|
+
this.enforceLimit("exceptions");
|
|
33
|
+
}
|
|
34
|
+
async saveExceptions(entries) {
|
|
35
|
+
this.exceptions.unshift(...entries);
|
|
36
|
+
this.enforceLimit("exceptions");
|
|
37
|
+
}
|
|
38
|
+
async getExceptions(options) {
|
|
39
|
+
return this.filterEntries(this.exceptions, options);
|
|
40
|
+
}
|
|
41
|
+
async getException(id) {
|
|
42
|
+
return this.exceptions.find((e) => e.id === id) ?? null;
|
|
43
|
+
}
|
|
44
|
+
async saveLog(entry) {
|
|
45
|
+
this.logs.unshift(entry);
|
|
46
|
+
this.enforceLimit("logs");
|
|
47
|
+
}
|
|
48
|
+
async saveLogs(entries) {
|
|
49
|
+
this.logs.unshift(...entries);
|
|
50
|
+
this.enforceLimit("logs");
|
|
51
|
+
}
|
|
52
|
+
async getLogs(options) {
|
|
53
|
+
return this.filterEntries(this.logs, options);
|
|
54
|
+
}
|
|
55
|
+
async prune(olderThan) {
|
|
56
|
+
const beforeCount = this.requests.length + this.exceptions.length + this.logs.length;
|
|
57
|
+
this.requests = this.requests.filter((r) => r.timestamp >= olderThan);
|
|
58
|
+
this.exceptions = this.exceptions.filter((e) => e.timestamp >= olderThan);
|
|
59
|
+
this.logs = this.logs.filter((l) => l.timestamp >= olderThan);
|
|
60
|
+
const afterCount = this.requests.length + this.exceptions.length + this.logs.length;
|
|
61
|
+
return beforeCount - afterCount;
|
|
62
|
+
}
|
|
63
|
+
async getStats() {
|
|
64
|
+
const allTimestamps = [
|
|
65
|
+
...this.requests.map((r) => r.timestamp),
|
|
66
|
+
...this.exceptions.map((e) => e.timestamp),
|
|
67
|
+
...this.logs.map((l) => l.timestamp)
|
|
68
|
+
].sort((a, b) => a.getTime() - b.getTime());
|
|
69
|
+
return {
|
|
70
|
+
requests: this.requests.length,
|
|
71
|
+
exceptions: this.exceptions.length,
|
|
72
|
+
logs: this.logs.length,
|
|
73
|
+
oldestEntry: allTimestamps[0],
|
|
74
|
+
newestEntry: allTimestamps[allTimestamps.length - 1]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
clear() {
|
|
78
|
+
this.requests = [];
|
|
79
|
+
this.exceptions = [];
|
|
80
|
+
this.logs = [];
|
|
81
|
+
}
|
|
82
|
+
enforceLimit(type) {
|
|
83
|
+
if (this[type].length > this.maxEntries) this[type] = this[type].slice(0, this.maxEntries);
|
|
84
|
+
}
|
|
85
|
+
filterEntries(entries, options) {
|
|
86
|
+
let result = entries;
|
|
87
|
+
if (options?.after) result = result.filter((e) => e.timestamp >= options.after);
|
|
88
|
+
if (options?.before) result = result.filter((e) => e.timestamp <= options.before);
|
|
89
|
+
if (options?.tags && options.tags.length > 0) result = result.filter((e) => e.tags && options.tags.some((t) => e.tags.includes(t)));
|
|
90
|
+
if (options?.search) {
|
|
91
|
+
const searchLower = options.search.toLowerCase();
|
|
92
|
+
result = result.filter((e) => {
|
|
93
|
+
const str = JSON.stringify(e).toLowerCase();
|
|
94
|
+
return str.includes(searchLower);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const offset = options?.offset ?? 0;
|
|
98
|
+
const limit = options?.limit ?? 50;
|
|
99
|
+
return result.slice(offset, offset + limit);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
//#endregion
|
|
104
|
+
Object.defineProperty(exports, 'InMemoryStorage', {
|
|
105
|
+
enumerable: true,
|
|
106
|
+
get: function () {
|
|
107
|
+
return InMemoryStorage;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
//# sourceMappingURL=memory-9-B9WACq.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-9-B9WACq.cjs","names":["options?: InMemoryStorageOptions","entry: RequestEntry","entries: RequestEntry[]","options?: QueryOptions","id: string","entry: ExceptionEntry","entries: ExceptionEntry[]","entry: LogEntry","entries: LogEntry[]","olderThan: Date","type: 'requests' | 'exceptions' | 'logs'","entries: T[]"],"sources":["../src/storage/memory.ts"],"sourcesContent":["import type {\n ExceptionEntry,\n LogEntry,\n QueryOptions,\n RequestEntry,\n TelescopeStats,\n TelescopeStorage,\n} from '../types';\n\nexport interface InMemoryStorageOptions {\n /** Maximum number of entries to keep per type (default: 1000) */\n maxEntries?: number;\n}\n\n/**\n * In-memory storage for Telescope data.\n * Ideal for development and testing.\n * Data is lost when the process restarts.\n */\nexport class InMemoryStorage implements TelescopeStorage {\n private requests: RequestEntry[] = [];\n private exceptions: ExceptionEntry[] = [];\n private logs: LogEntry[] = [];\n private maxEntries: number;\n\n constructor(options?: InMemoryStorageOptions) {\n this.maxEntries = options?.maxEntries ?? 1000;\n }\n\n // Requests\n\n async saveRequest(entry: RequestEntry): Promise<void> {\n this.requests.unshift(entry);\n this.enforceLimit('requests');\n }\n\n async saveRequests(entries: RequestEntry[]): Promise<void> {\n this.requests.unshift(...entries);\n this.enforceLimit('requests');\n }\n\n async getRequests(options?: QueryOptions): Promise<RequestEntry[]> {\n return this.filterEntries(this.requests, options);\n }\n\n async getRequest(id: string): Promise<RequestEntry | null> {\n return this.requests.find((r) => r.id === id) ?? null;\n }\n\n // Exceptions\n\n async saveException(entry: ExceptionEntry): Promise<void> {\n this.exceptions.unshift(entry);\n this.enforceLimit('exceptions');\n }\n\n async saveExceptions(entries: ExceptionEntry[]): Promise<void> {\n this.exceptions.unshift(...entries);\n this.enforceLimit('exceptions');\n }\n\n async getExceptions(options?: QueryOptions): Promise<ExceptionEntry[]> {\n return this.filterEntries(this.exceptions, options);\n }\n\n async getException(id: string): Promise<ExceptionEntry | null> {\n return this.exceptions.find((e) => e.id === id) ?? null;\n }\n\n // Logs\n\n async saveLog(entry: LogEntry): Promise<void> {\n this.logs.unshift(entry);\n this.enforceLimit('logs');\n }\n\n async saveLogs(entries: LogEntry[]): Promise<void> {\n this.logs.unshift(...entries);\n this.enforceLimit('logs');\n }\n\n async getLogs(options?: QueryOptions): Promise<LogEntry[]> {\n return this.filterEntries(this.logs, options);\n }\n\n // Cleanup\n\n async prune(olderThan: Date): Promise<number> {\n const beforeCount =\n this.requests.length + this.exceptions.length + this.logs.length;\n\n this.requests = this.requests.filter((r) => r.timestamp >= olderThan);\n this.exceptions = this.exceptions.filter((e) => e.timestamp >= olderThan);\n this.logs = this.logs.filter((l) => l.timestamp >= olderThan);\n\n const afterCount =\n this.requests.length + this.exceptions.length + this.logs.length;\n return beforeCount - afterCount;\n }\n\n // Stats\n\n async getStats(): Promise<TelescopeStats> {\n const allTimestamps = [\n ...this.requests.map((r) => r.timestamp),\n ...this.exceptions.map((e) => e.timestamp),\n ...this.logs.map((l) => l.timestamp),\n ].sort((a, b) => a.getTime() - b.getTime());\n\n return {\n requests: this.requests.length,\n exceptions: this.exceptions.length,\n logs: this.logs.length,\n oldestEntry: allTimestamps[0],\n newestEntry: allTimestamps[allTimestamps.length - 1],\n };\n }\n\n // Clear all data (useful for testing)\n\n clear(): void {\n this.requests = [];\n this.exceptions = [];\n this.logs = [];\n }\n\n // Private helpers\n\n private enforceLimit(type: 'requests' | 'exceptions' | 'logs'): void {\n if (this[type].length > this.maxEntries) {\n this[type] = this[type].slice(0, this.maxEntries);\n }\n }\n\n private filterEntries<T extends { timestamp: Date; tags?: string[] }>(\n entries: T[],\n options?: QueryOptions,\n ): T[] {\n let result = entries;\n\n if (options?.after) {\n result = result.filter((e) => e.timestamp >= options.after!);\n }\n\n if (options?.before) {\n result = result.filter((e) => e.timestamp <= options.before!);\n }\n\n if (options?.tags && options.tags.length > 0) {\n result = result.filter(\n (e) => e.tags && options.tags!.some((t) => e.tags!.includes(t)),\n );\n }\n\n if (options?.search) {\n const searchLower = options.search.toLowerCase();\n result = result.filter((e) => {\n const str = JSON.stringify(e).toLowerCase();\n return str.includes(searchLower);\n });\n }\n\n const offset = options?.offset ?? 0;\n const limit = options?.limit ?? 50;\n\n return result.slice(offset, offset + limit);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ExceptionEntry, LogEntry, QueryOptions, RequestEntry, TelescopeStats, TelescopeStorage } from "./types-CZbzz8kx.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/storage/memory.d.ts
|
|
4
|
+
interface InMemoryStorageOptions {
|
|
5
|
+
/** Maximum number of entries to keep per type (default: 1000) */
|
|
6
|
+
maxEntries?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* In-memory storage for Telescope data.
|
|
10
|
+
* Ideal for development and testing.
|
|
11
|
+
* Data is lost when the process restarts.
|
|
12
|
+
*/
|
|
13
|
+
declare class InMemoryStorage implements TelescopeStorage {
|
|
14
|
+
private requests;
|
|
15
|
+
private exceptions;
|
|
16
|
+
private logs;
|
|
17
|
+
private maxEntries;
|
|
18
|
+
constructor(options?: InMemoryStorageOptions);
|
|
19
|
+
saveRequest(entry: RequestEntry): Promise<void>;
|
|
20
|
+
saveRequests(entries: RequestEntry[]): Promise<void>;
|
|
21
|
+
getRequests(options?: QueryOptions): Promise<RequestEntry[]>;
|
|
22
|
+
getRequest(id: string): Promise<RequestEntry | null>;
|
|
23
|
+
saveException(entry: ExceptionEntry): Promise<void>;
|
|
24
|
+
saveExceptions(entries: ExceptionEntry[]): Promise<void>;
|
|
25
|
+
getExceptions(options?: QueryOptions): Promise<ExceptionEntry[]>;
|
|
26
|
+
getException(id: string): Promise<ExceptionEntry | null>;
|
|
27
|
+
saveLog(entry: LogEntry): Promise<void>;
|
|
28
|
+
saveLogs(entries: LogEntry[]): Promise<void>;
|
|
29
|
+
getLogs(options?: QueryOptions): Promise<LogEntry[]>;
|
|
30
|
+
prune(olderThan: Date): Promise<number>;
|
|
31
|
+
getStats(): Promise<TelescopeStats>;
|
|
32
|
+
clear(): void;
|
|
33
|
+
private enforceLimit;
|
|
34
|
+
private filterEntries;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { InMemoryStorage, InMemoryStorageOptions };
|
|
38
|
+
//# sourceMappingURL=memory-Cm0eevCS.d.mts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ExceptionEntry, LogEntry, QueryOptions, RequestEntry, TelescopeStats, TelescopeStorage } from "./types-BGDhFv4R.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/storage/memory.d.ts
|
|
4
|
+
interface InMemoryStorageOptions {
|
|
5
|
+
/** Maximum number of entries to keep per type (default: 1000) */
|
|
6
|
+
maxEntries?: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* In-memory storage for Telescope data.
|
|
10
|
+
* Ideal for development and testing.
|
|
11
|
+
* Data is lost when the process restarts.
|
|
12
|
+
*/
|
|
13
|
+
declare class InMemoryStorage implements TelescopeStorage {
|
|
14
|
+
private requests;
|
|
15
|
+
private exceptions;
|
|
16
|
+
private logs;
|
|
17
|
+
private maxEntries;
|
|
18
|
+
constructor(options?: InMemoryStorageOptions);
|
|
19
|
+
saveRequest(entry: RequestEntry): Promise<void>;
|
|
20
|
+
saveRequests(entries: RequestEntry[]): Promise<void>;
|
|
21
|
+
getRequests(options?: QueryOptions): Promise<RequestEntry[]>;
|
|
22
|
+
getRequest(id: string): Promise<RequestEntry | null>;
|
|
23
|
+
saveException(entry: ExceptionEntry): Promise<void>;
|
|
24
|
+
saveExceptions(entries: ExceptionEntry[]): Promise<void>;
|
|
25
|
+
getExceptions(options?: QueryOptions): Promise<ExceptionEntry[]>;
|
|
26
|
+
getException(id: string): Promise<ExceptionEntry | null>;
|
|
27
|
+
saveLog(entry: LogEntry): Promise<void>;
|
|
28
|
+
saveLogs(entries: LogEntry[]): Promise<void>;
|
|
29
|
+
getLogs(options?: QueryOptions): Promise<LogEntry[]>;
|
|
30
|
+
prune(olderThan: Date): Promise<number>;
|
|
31
|
+
getStats(): Promise<TelescopeStats>;
|
|
32
|
+
clear(): void;
|
|
33
|
+
private enforceLimit;
|
|
34
|
+
private filterEntries;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { InMemoryStorage, InMemoryStorageOptions };
|
|
38
|
+
//# sourceMappingURL=memory-DiP1a-pp.d.cts.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
//#region src/storage/memory.ts
|
|
2
|
+
/**
|
|
3
|
+
* In-memory storage for Telescope data.
|
|
4
|
+
* Ideal for development and testing.
|
|
5
|
+
* Data is lost when the process restarts.
|
|
6
|
+
*/
|
|
7
|
+
var InMemoryStorage = class {
|
|
8
|
+
requests = [];
|
|
9
|
+
exceptions = [];
|
|
10
|
+
logs = [];
|
|
11
|
+
maxEntries;
|
|
12
|
+
constructor(options) {
|
|
13
|
+
this.maxEntries = options?.maxEntries ?? 1e3;
|
|
14
|
+
}
|
|
15
|
+
async saveRequest(entry) {
|
|
16
|
+
this.requests.unshift(entry);
|
|
17
|
+
this.enforceLimit("requests");
|
|
18
|
+
}
|
|
19
|
+
async saveRequests(entries) {
|
|
20
|
+
this.requests.unshift(...entries);
|
|
21
|
+
this.enforceLimit("requests");
|
|
22
|
+
}
|
|
23
|
+
async getRequests(options) {
|
|
24
|
+
return this.filterEntries(this.requests, options);
|
|
25
|
+
}
|
|
26
|
+
async getRequest(id) {
|
|
27
|
+
return this.requests.find((r) => r.id === id) ?? null;
|
|
28
|
+
}
|
|
29
|
+
async saveException(entry) {
|
|
30
|
+
this.exceptions.unshift(entry);
|
|
31
|
+
this.enforceLimit("exceptions");
|
|
32
|
+
}
|
|
33
|
+
async saveExceptions(entries) {
|
|
34
|
+
this.exceptions.unshift(...entries);
|
|
35
|
+
this.enforceLimit("exceptions");
|
|
36
|
+
}
|
|
37
|
+
async getExceptions(options) {
|
|
38
|
+
return this.filterEntries(this.exceptions, options);
|
|
39
|
+
}
|
|
40
|
+
async getException(id) {
|
|
41
|
+
return this.exceptions.find((e) => e.id === id) ?? null;
|
|
42
|
+
}
|
|
43
|
+
async saveLog(entry) {
|
|
44
|
+
this.logs.unshift(entry);
|
|
45
|
+
this.enforceLimit("logs");
|
|
46
|
+
}
|
|
47
|
+
async saveLogs(entries) {
|
|
48
|
+
this.logs.unshift(...entries);
|
|
49
|
+
this.enforceLimit("logs");
|
|
50
|
+
}
|
|
51
|
+
async getLogs(options) {
|
|
52
|
+
return this.filterEntries(this.logs, options);
|
|
53
|
+
}
|
|
54
|
+
async prune(olderThan) {
|
|
55
|
+
const beforeCount = this.requests.length + this.exceptions.length + this.logs.length;
|
|
56
|
+
this.requests = this.requests.filter((r) => r.timestamp >= olderThan);
|
|
57
|
+
this.exceptions = this.exceptions.filter((e) => e.timestamp >= olderThan);
|
|
58
|
+
this.logs = this.logs.filter((l) => l.timestamp >= olderThan);
|
|
59
|
+
const afterCount = this.requests.length + this.exceptions.length + this.logs.length;
|
|
60
|
+
return beforeCount - afterCount;
|
|
61
|
+
}
|
|
62
|
+
async getStats() {
|
|
63
|
+
const allTimestamps = [
|
|
64
|
+
...this.requests.map((r) => r.timestamp),
|
|
65
|
+
...this.exceptions.map((e) => e.timestamp),
|
|
66
|
+
...this.logs.map((l) => l.timestamp)
|
|
67
|
+
].sort((a, b) => a.getTime() - b.getTime());
|
|
68
|
+
return {
|
|
69
|
+
requests: this.requests.length,
|
|
70
|
+
exceptions: this.exceptions.length,
|
|
71
|
+
logs: this.logs.length,
|
|
72
|
+
oldestEntry: allTimestamps[0],
|
|
73
|
+
newestEntry: allTimestamps[allTimestamps.length - 1]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
clear() {
|
|
77
|
+
this.requests = [];
|
|
78
|
+
this.exceptions = [];
|
|
79
|
+
this.logs = [];
|
|
80
|
+
}
|
|
81
|
+
enforceLimit(type) {
|
|
82
|
+
if (this[type].length > this.maxEntries) this[type] = this[type].slice(0, this.maxEntries);
|
|
83
|
+
}
|
|
84
|
+
filterEntries(entries, options) {
|
|
85
|
+
let result = entries;
|
|
86
|
+
if (options?.after) result = result.filter((e) => e.timestamp >= options.after);
|
|
87
|
+
if (options?.before) result = result.filter((e) => e.timestamp <= options.before);
|
|
88
|
+
if (options?.tags && options.tags.length > 0) result = result.filter((e) => e.tags && options.tags.some((t) => e.tags.includes(t)));
|
|
89
|
+
if (options?.search) {
|
|
90
|
+
const searchLower = options.search.toLowerCase();
|
|
91
|
+
result = result.filter((e) => {
|
|
92
|
+
const str = JSON.stringify(e).toLowerCase();
|
|
93
|
+
return str.includes(searchLower);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
const offset = options?.offset ?? 0;
|
|
97
|
+
const limit = options?.limit ?? 50;
|
|
98
|
+
return result.slice(offset, offset + limit);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
//#endregion
|
|
103
|
+
export { InMemoryStorage };
|
|
104
|
+
//# sourceMappingURL=memory-SdN5vtG9.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-SdN5vtG9.mjs","names":["options?: InMemoryStorageOptions","entry: RequestEntry","entries: RequestEntry[]","options?: QueryOptions","id: string","entry: ExceptionEntry","entries: ExceptionEntry[]","entry: LogEntry","entries: LogEntry[]","olderThan: Date","type: 'requests' | 'exceptions' | 'logs'","entries: T[]"],"sources":["../src/storage/memory.ts"],"sourcesContent":["import type {\n ExceptionEntry,\n LogEntry,\n QueryOptions,\n RequestEntry,\n TelescopeStats,\n TelescopeStorage,\n} from '../types';\n\nexport interface InMemoryStorageOptions {\n /** Maximum number of entries to keep per type (default: 1000) */\n maxEntries?: number;\n}\n\n/**\n * In-memory storage for Telescope data.\n * Ideal for development and testing.\n * Data is lost when the process restarts.\n */\nexport class InMemoryStorage implements TelescopeStorage {\n private requests: RequestEntry[] = [];\n private exceptions: ExceptionEntry[] = [];\n private logs: LogEntry[] = [];\n private maxEntries: number;\n\n constructor(options?: InMemoryStorageOptions) {\n this.maxEntries = options?.maxEntries ?? 1000;\n }\n\n // Requests\n\n async saveRequest(entry: RequestEntry): Promise<void> {\n this.requests.unshift(entry);\n this.enforceLimit('requests');\n }\n\n async saveRequests(entries: RequestEntry[]): Promise<void> {\n this.requests.unshift(...entries);\n this.enforceLimit('requests');\n }\n\n async getRequests(options?: QueryOptions): Promise<RequestEntry[]> {\n return this.filterEntries(this.requests, options);\n }\n\n async getRequest(id: string): Promise<RequestEntry | null> {\n return this.requests.find((r) => r.id === id) ?? null;\n }\n\n // Exceptions\n\n async saveException(entry: ExceptionEntry): Promise<void> {\n this.exceptions.unshift(entry);\n this.enforceLimit('exceptions');\n }\n\n async saveExceptions(entries: ExceptionEntry[]): Promise<void> {\n this.exceptions.unshift(...entries);\n this.enforceLimit('exceptions');\n }\n\n async getExceptions(options?: QueryOptions): Promise<ExceptionEntry[]> {\n return this.filterEntries(this.exceptions, options);\n }\n\n async getException(id: string): Promise<ExceptionEntry | null> {\n return this.exceptions.find((e) => e.id === id) ?? null;\n }\n\n // Logs\n\n async saveLog(entry: LogEntry): Promise<void> {\n this.logs.unshift(entry);\n this.enforceLimit('logs');\n }\n\n async saveLogs(entries: LogEntry[]): Promise<void> {\n this.logs.unshift(...entries);\n this.enforceLimit('logs');\n }\n\n async getLogs(options?: QueryOptions): Promise<LogEntry[]> {\n return this.filterEntries(this.logs, options);\n }\n\n // Cleanup\n\n async prune(olderThan: Date): Promise<number> {\n const beforeCount =\n this.requests.length + this.exceptions.length + this.logs.length;\n\n this.requests = this.requests.filter((r) => r.timestamp >= olderThan);\n this.exceptions = this.exceptions.filter((e) => e.timestamp >= olderThan);\n this.logs = this.logs.filter((l) => l.timestamp >= olderThan);\n\n const afterCount =\n this.requests.length + this.exceptions.length + this.logs.length;\n return beforeCount - afterCount;\n }\n\n // Stats\n\n async getStats(): Promise<TelescopeStats> {\n const allTimestamps = [\n ...this.requests.map((r) => r.timestamp),\n ...this.exceptions.map((e) => e.timestamp),\n ...this.logs.map((l) => l.timestamp),\n ].sort((a, b) => a.getTime() - b.getTime());\n\n return {\n requests: this.requests.length,\n exceptions: this.exceptions.length,\n logs: this.logs.length,\n oldestEntry: allTimestamps[0],\n newestEntry: allTimestamps[allTimestamps.length - 1],\n };\n }\n\n // Clear all data (useful for testing)\n\n clear(): void {\n this.requests = [];\n this.exceptions = [];\n this.logs = [];\n }\n\n // Private helpers\n\n private enforceLimit(type: 'requests' | 'exceptions' | 'logs'): void {\n if (this[type].length > this.maxEntries) {\n this[type] = this[type].slice(0, this.maxEntries);\n }\n }\n\n private filterEntries<T extends { timestamp: Date; tags?: string[] }>(\n entries: T[],\n options?: QueryOptions,\n ): T[] {\n let result = entries;\n\n if (options?.after) {\n result = result.filter((e) => e.timestamp >= options.after!);\n }\n\n if (options?.before) {\n result = result.filter((e) => e.timestamp <= options.before!);\n }\n\n if (options?.tags && options.tags.length > 0) {\n result = result.filter(\n (e) => e.tags && options.tags!.some((t) => e.tags!.includes(t)),\n );\n }\n\n if (options?.search) {\n const searchLower = options.search.toLowerCase();\n result = result.filter((e) => {\n const str = JSON.stringify(e).toLowerCase();\n return str.includes(searchLower);\n });\n }\n\n const offset = options?.offset ?? 0;\n const limit = options?.limit ?? 50;\n\n return result.slice(offset, offset + limit);\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const require_chunk = require('../chunk-CUT6urMc.cjs');
|
|
2
|
+
const require_ui_assets = require('../ui-assets-ulevVble.cjs');
|
|
3
|
+
const hono = require_chunk.__toESM(require("hono"));
|
|
4
|
+
|
|
5
|
+
//#region src/server/hono.ts
|
|
6
|
+
const CONTEXT_KEY = "telescope-request-id";
|
|
7
|
+
/**
|
|
8
|
+
* Create Hono middleware that captures requests and responses
|
|
9
|
+
*/
|
|
10
|
+
function createMiddleware(telescope) {
|
|
11
|
+
return async (c, next) => {
|
|
12
|
+
if (!telescope.enabled) return next();
|
|
13
|
+
if (telescope.shouldIgnore(c.req.path)) return next();
|
|
14
|
+
const startTime = performance.now();
|
|
15
|
+
const headers = {};
|
|
16
|
+
c.req.raw.headers.forEach((value, key) => {
|
|
17
|
+
headers[key] = value;
|
|
18
|
+
});
|
|
19
|
+
const url = new URL(c.req.url);
|
|
20
|
+
const query = {};
|
|
21
|
+
url.searchParams.forEach((value, key) => {
|
|
22
|
+
query[key] = value;
|
|
23
|
+
});
|
|
24
|
+
let body;
|
|
25
|
+
if (telescope.recordBody && [
|
|
26
|
+
"POST",
|
|
27
|
+
"PUT",
|
|
28
|
+
"PATCH"
|
|
29
|
+
].includes(c.req.method)) try {
|
|
30
|
+
const contentType = c.req.header("content-type") || "";
|
|
31
|
+
if (contentType.includes("application/json")) body = await c.req.json();
|
|
32
|
+
else if (contentType.includes("application/x-www-form-urlencoded")) {
|
|
33
|
+
const formData = await c.req.formData();
|
|
34
|
+
body = Object.fromEntries(formData.entries());
|
|
35
|
+
} else if (contentType.includes("text/")) body = await c.req.text();
|
|
36
|
+
} catch {}
|
|
37
|
+
const ip = c.req.header("x-forwarded-for") || c.req.header("x-real-ip");
|
|
38
|
+
try {
|
|
39
|
+
await next();
|
|
40
|
+
const duration = performance.now() - startTime;
|
|
41
|
+
const responseHeaders = {};
|
|
42
|
+
c.res.headers.forEach((value, key) => {
|
|
43
|
+
responseHeaders[key] = value;
|
|
44
|
+
});
|
|
45
|
+
let responseBody;
|
|
46
|
+
if (telescope.recordBody) try {
|
|
47
|
+
const contentType = c.res.headers.get("content-type") || "";
|
|
48
|
+
if (contentType.includes("application/json")) {
|
|
49
|
+
const cloned = c.res.clone();
|
|
50
|
+
responseBody = await cloned.json();
|
|
51
|
+
}
|
|
52
|
+
} catch {}
|
|
53
|
+
const requestId = await telescope.recordRequest({
|
|
54
|
+
method: c.req.method,
|
|
55
|
+
path: c.req.path,
|
|
56
|
+
url: c.req.url,
|
|
57
|
+
headers,
|
|
58
|
+
body,
|
|
59
|
+
query,
|
|
60
|
+
status: c.res.status,
|
|
61
|
+
responseHeaders,
|
|
62
|
+
responseBody,
|
|
63
|
+
duration,
|
|
64
|
+
ip
|
|
65
|
+
});
|
|
66
|
+
c.set(CONTEXT_KEY, requestId);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
await telescope.exception(error);
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse query options from Hono context
|
|
75
|
+
*/
|
|
76
|
+
function parseQueryOptions(c) {
|
|
77
|
+
const limit = parseInt(c.req.query("limit") || "50", 10);
|
|
78
|
+
const offset = parseInt(c.req.query("offset") || "0", 10);
|
|
79
|
+
const search = c.req.query("search");
|
|
80
|
+
const before = c.req.query("before");
|
|
81
|
+
const after = c.req.query("after");
|
|
82
|
+
const tags = c.req.query("tags")?.split(",").filter(Boolean);
|
|
83
|
+
return {
|
|
84
|
+
limit: Math.min(limit, 100),
|
|
85
|
+
offset,
|
|
86
|
+
search,
|
|
87
|
+
before: before ? new Date(before) : void 0,
|
|
88
|
+
after: after ? new Date(after) : void 0,
|
|
89
|
+
tags
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Create Hono app with dashboard UI and API routes
|
|
94
|
+
*/
|
|
95
|
+
function createUI(telescope) {
|
|
96
|
+
const app = new hono.Hono();
|
|
97
|
+
app.get("/api/requests", async (c) => {
|
|
98
|
+
const options = parseQueryOptions(c);
|
|
99
|
+
const requests = await telescope.getRequests(options);
|
|
100
|
+
return c.json(requests);
|
|
101
|
+
});
|
|
102
|
+
app.get("/api/requests/:id", async (c) => {
|
|
103
|
+
const request = await telescope.getRequest(c.req.param("id"));
|
|
104
|
+
if (!request) return c.json({ error: "Request not found" }, 404);
|
|
105
|
+
return c.json(request);
|
|
106
|
+
});
|
|
107
|
+
app.get("/api/exceptions", async (c) => {
|
|
108
|
+
const options = parseQueryOptions(c);
|
|
109
|
+
const exceptions = await telescope.getExceptions(options);
|
|
110
|
+
return c.json(exceptions);
|
|
111
|
+
});
|
|
112
|
+
app.get("/api/exceptions/:id", async (c) => {
|
|
113
|
+
const exception = await telescope.getException(c.req.param("id"));
|
|
114
|
+
if (!exception) return c.json({ error: "Exception not found" }, 404);
|
|
115
|
+
return c.json(exception);
|
|
116
|
+
});
|
|
117
|
+
app.get("/api/logs", async (c) => {
|
|
118
|
+
const options = parseQueryOptions(c);
|
|
119
|
+
const logs = await telescope.getLogs(options);
|
|
120
|
+
return c.json(logs);
|
|
121
|
+
});
|
|
122
|
+
app.get("/api/stats", async (c) => {
|
|
123
|
+
const stats = await telescope.getStats();
|
|
124
|
+
return c.json(stats);
|
|
125
|
+
});
|
|
126
|
+
app.get("/assets/:filename", (c) => {
|
|
127
|
+
const filename = c.req.param("filename");
|
|
128
|
+
const assetPath = `assets/${filename}`;
|
|
129
|
+
const asset = require_ui_assets.getAsset(assetPath);
|
|
130
|
+
if (asset) return c.body(asset.content, 200, {
|
|
131
|
+
"Content-Type": asset.contentType,
|
|
132
|
+
"Cache-Control": "public, max-age=31536000, immutable"
|
|
133
|
+
});
|
|
134
|
+
return c.notFound();
|
|
135
|
+
});
|
|
136
|
+
app.get("/", (c) => {
|
|
137
|
+
const html = require_ui_assets.getIndexHtml();
|
|
138
|
+
if (html) return c.html(html);
|
|
139
|
+
return c.html(telescope.getDashboardHtml());
|
|
140
|
+
});
|
|
141
|
+
app.get("/*", (c) => {
|
|
142
|
+
const html = require_ui_assets.getIndexHtml();
|
|
143
|
+
if (html) return c.html(html);
|
|
144
|
+
return c.html(telescope.getDashboardHtml());
|
|
145
|
+
});
|
|
146
|
+
return app;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Set up WebSocket routes for real-time updates.
|
|
150
|
+
* Requires @hono/node-ws for Node.js or Bun's built-in WebSocket.
|
|
151
|
+
*/
|
|
152
|
+
function setupWebSocket(app, telescope, upgradeWebSocket) {
|
|
153
|
+
app.get("/ws", upgradeWebSocket(() => ({
|
|
154
|
+
onOpen: (_event, ws) => {
|
|
155
|
+
telescope.addWsClient(ws);
|
|
156
|
+
},
|
|
157
|
+
onClose: (_event, ws) => {
|
|
158
|
+
telescope.removeWsClient(ws);
|
|
159
|
+
},
|
|
160
|
+
onMessage: (event, ws) => {
|
|
161
|
+
try {
|
|
162
|
+
const data = JSON.parse(event.data);
|
|
163
|
+
if (data.type === "ping") ws.send(JSON.stringify({ type: "pong" }));
|
|
164
|
+
} catch {}
|
|
165
|
+
}
|
|
166
|
+
})));
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Get the request ID from Hono context (set by middleware)
|
|
170
|
+
*/
|
|
171
|
+
function getRequestId(c) {
|
|
172
|
+
return c.get(CONTEXT_KEY);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
//#endregion
|
|
176
|
+
exports.createMiddleware = createMiddleware;
|
|
177
|
+
exports.createUI = createUI;
|
|
178
|
+
exports.getRequestId = getRequestId;
|
|
179
|
+
exports.setupWebSocket = setupWebSocket;
|
|
180
|
+
//# sourceMappingURL=hono.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hono.cjs","names":["telescope: Telescope","c: Context","next: Next","headers: Record<string, string>","query: Record<string, string>","body: unknown","responseHeaders: Record<string, string>","responseBody: unknown","Hono","app: Hono","upgradeWebSocket: (handler: any) => any","_event: Event","ws: WebSocket","event: MessageEvent"],"sources":["../../src/server/hono.ts"],"sourcesContent":["import { Hono } from 'hono';\nimport type { Context, MiddlewareHandler, Next } from 'hono';\nimport type { Telescope } from '../Telescope';\nimport type { QueryOptions } from '../types';\nimport { getAsset, getIndexHtml } from '../ui-assets';\n\nconst CONTEXT_KEY = 'telescope-request-id';\n\n/**\n * Create Hono middleware that captures requests and responses\n */\nexport function createMiddleware(telescope: Telescope): MiddlewareHandler {\n return async (c: Context, next: Next) => {\n if (!telescope.enabled) {\n return next();\n }\n\n if (telescope.shouldIgnore(c.req.path)) {\n return next();\n }\n\n const startTime = performance.now();\n\n // Capture request data\n const headers: Record<string, string> = {};\n c.req.raw.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n const url = new URL(c.req.url);\n const query: Record<string, string> = {};\n url.searchParams.forEach((value, key) => {\n query[key] = value;\n });\n\n let body: unknown;\n if (\n telescope.recordBody &&\n ['POST', 'PUT', 'PATCH'].includes(c.req.method)\n ) {\n try {\n const contentType = c.req.header('content-type') || '';\n if (contentType.includes('application/json')) {\n body = await c.req.json();\n } else if (contentType.includes('application/x-www-form-urlencoded')) {\n const formData = await c.req.formData();\n body = Object.fromEntries(formData.entries());\n } else if (contentType.includes('text/')) {\n body = await c.req.text();\n }\n } catch {\n // Ignore body parsing errors\n }\n }\n\n const ip = c.req.header('x-forwarded-for') || c.req.header('x-real-ip');\n\n try {\n await next();\n\n // Capture response data\n const duration = performance.now() - startTime;\n\n const responseHeaders: Record<string, string> = {};\n c.res.headers.forEach((value, key) => {\n responseHeaders[key] = value;\n });\n\n let responseBody: unknown;\n if (telescope.recordBody) {\n try {\n const contentType = c.res.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n const cloned = c.res.clone();\n responseBody = await cloned.json();\n }\n } catch {\n // Ignore body parsing errors\n }\n }\n\n const requestId = await telescope.recordRequest({\n method: c.req.method,\n path: c.req.path,\n url: c.req.url,\n headers,\n body,\n query,\n status: c.res.status,\n responseHeaders,\n responseBody,\n duration,\n ip,\n });\n\n c.set(CONTEXT_KEY, requestId);\n } catch (error) {\n await telescope.exception(error as Error);\n throw error;\n }\n };\n}\n\n/**\n * Parse query options from Hono context\n */\nfunction parseQueryOptions(c: Context): QueryOptions {\n const limit = parseInt(c.req.query('limit') || '50', 10);\n const offset = parseInt(c.req.query('offset') || '0', 10);\n const search = c.req.query('search');\n const before = c.req.query('before');\n const after = c.req.query('after');\n const tags = c.req.query('tags')?.split(',').filter(Boolean);\n\n return {\n limit: Math.min(limit, 100),\n offset,\n search,\n before: before ? new Date(before) : undefined,\n after: after ? new Date(after) : undefined,\n tags,\n };\n}\n\n/**\n * Create Hono app with dashboard UI and API routes\n */\nexport function createUI(telescope: Telescope): Hono {\n const app = new Hono();\n\n // API routes\n app.get('/api/requests', async (c) => {\n const options = parseQueryOptions(c);\n const requests = await telescope.getRequests(options);\n return c.json(requests);\n });\n\n app.get('/api/requests/:id', async (c) => {\n const request = await telescope.getRequest(c.req.param('id'));\n if (!request) {\n return c.json({ error: 'Request not found' }, 404);\n }\n return c.json(request);\n });\n\n app.get('/api/exceptions', async (c) => {\n const options = parseQueryOptions(c);\n const exceptions = await telescope.getExceptions(options);\n return c.json(exceptions);\n });\n\n app.get('/api/exceptions/:id', async (c) => {\n const exception = await telescope.getException(c.req.param('id'));\n if (!exception) {\n return c.json({ error: 'Exception not found' }, 404);\n }\n return c.json(exception);\n });\n\n app.get('/api/logs', async (c) => {\n const options = parseQueryOptions(c);\n const logs = await telescope.getLogs(options);\n return c.json(logs);\n });\n\n app.get('/api/stats', async (c) => {\n const stats = await telescope.getStats();\n return c.json(stats);\n });\n\n // Static assets\n app.get('/assets/:filename', (c) => {\n const filename = c.req.param('filename');\n const assetPath = `assets/${filename}`;\n const asset = getAsset(assetPath);\n if (asset) {\n return c.body(asset.content, 200, {\n 'Content-Type': asset.contentType,\n 'Cache-Control': 'public, max-age=31536000, immutable',\n });\n }\n return c.notFound();\n });\n\n // Dashboard UI - serve React app\n app.get('/', (c) => {\n const html = getIndexHtml();\n if (html) {\n return c.html(html);\n }\n // Fallback to inline HTML if UI assets not available\n return c.html(telescope.getDashboardHtml());\n });\n\n app.get('/*', (c) => {\n // SPA fallback - serve index.html for client-side routing\n const html = getIndexHtml();\n if (html) {\n return c.html(html);\n }\n return c.html(telescope.getDashboardHtml());\n });\n\n return app;\n}\n\n/**\n * Set up WebSocket routes for real-time updates.\n * Requires @hono/node-ws for Node.js or Bun's built-in WebSocket.\n */\nexport function setupWebSocket(\n app: Hono,\n telescope: Telescope,\n upgradeWebSocket: (handler: any) => any,\n): void {\n app.get(\n '/ws',\n upgradeWebSocket(() => ({\n onOpen: (_event: Event, ws: WebSocket) => {\n telescope.addWsClient(ws);\n },\n onClose: (_event: Event, ws: WebSocket) => {\n telescope.removeWsClient(ws);\n },\n onMessage: (event: MessageEvent, ws: WebSocket) => {\n try {\n const data = JSON.parse(event.data);\n if (data.type === 'ping') {\n ws.send(JSON.stringify({ type: 'pong' }));\n }\n } catch {\n // Ignore invalid messages\n }\n },\n })),\n );\n}\n\n/**\n * Get the request ID from Hono context (set by middleware)\n */\nexport function getRequestId(c: Context): string | undefined {\n return c.get(CONTEXT_KEY);\n}\n\n// Re-export types\nexport type { Telescope };\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "../types-BGDhFv4R.cjs";
|
|
2
|
+
import { Telescope } from "../Telescope-C5dyDYYB.cjs";
|
|
3
|
+
import { Context, Hono, MiddlewareHandler } from "hono";
|
|
4
|
+
|
|
5
|
+
//#region src/server/hono.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create Hono middleware that captures requests and responses
|
|
9
|
+
*/
|
|
10
|
+
declare function createMiddleware(telescope: Telescope): MiddlewareHandler;
|
|
11
|
+
/**
|
|
12
|
+
* Create Hono app with dashboard UI and API routes
|
|
13
|
+
*/
|
|
14
|
+
declare function createUI(telescope: Telescope): Hono;
|
|
15
|
+
/**
|
|
16
|
+
* Set up WebSocket routes for real-time updates.
|
|
17
|
+
* Requires @hono/node-ws for Node.js or Bun's built-in WebSocket.
|
|
18
|
+
*/
|
|
19
|
+
declare function setupWebSocket(app: Hono, telescope: Telescope, upgradeWebSocket: (handler: any) => any): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get the request ID from Hono context (set by middleware)
|
|
22
|
+
*/
|
|
23
|
+
declare function getRequestId(c: Context): string | undefined;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { Telescope, createMiddleware, createUI, getRequestId, setupWebSocket };
|
|
26
|
+
//# sourceMappingURL=hono.d.cts.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "../types-CZbzz8kx.mjs";
|
|
2
|
+
import { Telescope } from "../Telescope-DyIWgh9-.mjs";
|
|
3
|
+
import { Context, Hono, MiddlewareHandler } from "hono";
|
|
4
|
+
|
|
5
|
+
//#region src/server/hono.d.ts
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Create Hono middleware that captures requests and responses
|
|
9
|
+
*/
|
|
10
|
+
declare function createMiddleware(telescope: Telescope): MiddlewareHandler;
|
|
11
|
+
/**
|
|
12
|
+
* Create Hono app with dashboard UI and API routes
|
|
13
|
+
*/
|
|
14
|
+
declare function createUI(telescope: Telescope): Hono;
|
|
15
|
+
/**
|
|
16
|
+
* Set up WebSocket routes for real-time updates.
|
|
17
|
+
* Requires @hono/node-ws for Node.js or Bun's built-in WebSocket.
|
|
18
|
+
*/
|
|
19
|
+
declare function setupWebSocket(app: Hono, telescope: Telescope, upgradeWebSocket: (handler: any) => any): void;
|
|
20
|
+
/**
|
|
21
|
+
* Get the request ID from Hono context (set by middleware)
|
|
22
|
+
*/
|
|
23
|
+
declare function getRequestId(c: Context): string | undefined;
|
|
24
|
+
//#endregion
|
|
25
|
+
export { Telescope, createMiddleware, createUI, getRequestId, setupWebSocket };
|
|
26
|
+
//# sourceMappingURL=hono.d.mts.map
|