@geekmidas/studio 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.
Files changed (64) hide show
  1. package/dist/DataBrowser-DQ3-ZxdV.mjs +427 -0
  2. package/dist/DataBrowser-DQ3-ZxdV.mjs.map +1 -0
  3. package/dist/DataBrowser-SOcqmZb2.d.mts +267 -0
  4. package/dist/DataBrowser-c-Gs6PZB.cjs +432 -0
  5. package/dist/DataBrowser-c-Gs6PZB.cjs.map +1 -0
  6. package/dist/DataBrowser-hGwiTffZ.d.cts +267 -0
  7. package/dist/chunk-CUT6urMc.cjs +30 -0
  8. package/dist/data/index.cjs +4 -0
  9. package/dist/data/index.d.cts +2 -0
  10. package/dist/data/index.d.mts +2 -0
  11. package/dist/data/index.mjs +4 -0
  12. package/dist/index.cjs +239 -0
  13. package/dist/index.cjs.map +1 -0
  14. package/dist/index.d.cts +132 -0
  15. package/dist/index.d.mts +132 -0
  16. package/dist/index.mjs +230 -0
  17. package/dist/index.mjs.map +1 -0
  18. package/dist/server/hono.cjs +192 -0
  19. package/dist/server/hono.cjs.map +1 -0
  20. package/dist/server/hono.d.cts +19 -0
  21. package/dist/server/hono.d.mts +19 -0
  22. package/dist/server/hono.mjs +191 -0
  23. package/dist/server/hono.mjs.map +1 -0
  24. package/dist/types-BZv87Ikv.mjs +31 -0
  25. package/dist/types-BZv87Ikv.mjs.map +1 -0
  26. package/dist/types-CMttUZYk.cjs +43 -0
  27. package/dist/types-CMttUZYk.cjs.map +1 -0
  28. package/package.json +54 -0
  29. package/src/Studio.ts +318 -0
  30. package/src/data/DataBrowser.ts +166 -0
  31. package/src/data/__tests__/DataBrowser.integration.spec.ts +418 -0
  32. package/src/data/__tests__/filtering.integration.spec.ts +741 -0
  33. package/src/data/__tests__/introspection.integration.spec.ts +352 -0
  34. package/src/data/filtering.ts +191 -0
  35. package/src/data/index.ts +1 -0
  36. package/src/data/introspection.ts +220 -0
  37. package/src/data/pagination.ts +33 -0
  38. package/src/index.ts +31 -0
  39. package/src/server/__tests__/hono.integration.spec.ts +361 -0
  40. package/src/server/hono.ts +225 -0
  41. package/src/types.ts +278 -0
  42. package/src/ui-assets.ts +40 -0
  43. package/tsdown.config.ts +13 -0
  44. package/ui/index.html +12 -0
  45. package/ui/node_modules/.bin/browserslist +21 -0
  46. package/ui/node_modules/.bin/jiti +21 -0
  47. package/ui/node_modules/.bin/terser +21 -0
  48. package/ui/node_modules/.bin/tsc +21 -0
  49. package/ui/node_modules/.bin/tsserver +21 -0
  50. package/ui/node_modules/.bin/tsx +21 -0
  51. package/ui/node_modules/.bin/vite +21 -0
  52. package/ui/package.json +24 -0
  53. package/ui/src/App.tsx +141 -0
  54. package/ui/src/api.ts +71 -0
  55. package/ui/src/components/RowDetail.tsx +113 -0
  56. package/ui/src/components/TableList.tsx +51 -0
  57. package/ui/src/components/TableView.tsx +219 -0
  58. package/ui/src/main.tsx +10 -0
  59. package/ui/src/styles.css +36 -0
  60. package/ui/src/types.ts +50 -0
  61. package/ui/src/vite-env.d.ts +1 -0
  62. package/ui/tsconfig.json +21 -0
  63. package/ui/tsconfig.tsbuildinfo +1 -0
  64. package/ui/vite.config.ts +12 -0
@@ -0,0 +1,132 @@
1
+ import { ColumnInfo, ColumnType, CursorConfig, DataBrowser, DataBrowserOptions, Direction, FilterCondition, FilterOperator, MonitoringOptions, NormalizedStudioOptions, QueryOptions, QueryResult, SchemaInfo, SortConfig, StudioEvent, StudioEventType, StudioOptions, TableCursorConfig, TableInfo } from "./DataBrowser-hGwiTffZ.cjs";
2
+ import { Telescope, TelescopeStorage as MonitoringStorage } from "@geekmidas/telescope";
3
+ import { InMemoryStorage as InMemoryMonitoringStorage } from "@geekmidas/telescope/storage/memory";
4
+
5
+ //#region src/Studio.d.ts
6
+
7
+ /**
8
+ * Unified development tools dashboard combining monitoring and database browsing.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { Studio, Direction } from '@geekmidas/studio';
13
+ * import { InMemoryStorage } from '@geekmidas/telescope/storage/memory';
14
+ *
15
+ * const studio = new Studio({
16
+ * monitoring: {
17
+ * storage: new InMemoryStorage(),
18
+ * },
19
+ * data: {
20
+ * db: kyselyInstance,
21
+ * cursor: { field: 'id', direction: Direction.Desc },
22
+ * },
23
+ * });
24
+ * ```
25
+ */
26
+ declare class Studio<DB = unknown> {
27
+ private telescope;
28
+ private dataBrowser;
29
+ private options;
30
+ private wsClients;
31
+ constructor(options: StudioOptions<DB>);
32
+ /**
33
+ * Get the Studio dashboard path.
34
+ */
35
+ get path(): string;
36
+ /**
37
+ * Check if Studio is enabled.
38
+ */
39
+ get enabled(): boolean;
40
+ /**
41
+ * Get the data browser instance.
42
+ */
43
+ get data(): DataBrowser<DB>;
44
+ /**
45
+ * Check if body recording is enabled for monitoring.
46
+ */
47
+ get recordBody(): boolean;
48
+ /**
49
+ * Get max body size for monitoring.
50
+ */
51
+ get maxBodySize(): number;
52
+ /**
53
+ * Record a request entry.
54
+ */
55
+ recordRequest(entry: Parameters<Telescope['recordRequest']>[0]): Promise<string>;
56
+ /**
57
+ * Record log entries in batch.
58
+ */
59
+ log(entries: Parameters<Telescope['log']>[0]): Promise<void>;
60
+ /**
61
+ * Log a debug message.
62
+ */
63
+ debug(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
64
+ /**
65
+ * Log an info message.
66
+ */
67
+ info(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
68
+ /**
69
+ * Log a warning message.
70
+ */
71
+ warn(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
72
+ /**
73
+ * Log an error message.
74
+ */
75
+ error(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
76
+ /**
77
+ * Record an exception.
78
+ */
79
+ exception(error: Error, requestId?: string): Promise<void>;
80
+ /**
81
+ * Get requests from storage.
82
+ */
83
+ getRequests(options?: Parameters<Telescope['getRequests']>[0]): ReturnType<Telescope['getRequests']>;
84
+ /**
85
+ * Get a single request by ID.
86
+ */
87
+ getRequest(id: string): ReturnType<Telescope['getRequest']>;
88
+ /**
89
+ * Get exceptions from storage.
90
+ */
91
+ getExceptions(options?: Parameters<Telescope['getExceptions']>[0]): ReturnType<Telescope['getExceptions']>;
92
+ /**
93
+ * Get a single exception by ID.
94
+ */
95
+ getException(id: string): ReturnType<Telescope['getException']>;
96
+ /**
97
+ * Get logs from storage.
98
+ */
99
+ getLogs(options?: Parameters<Telescope['getLogs']>[0]): ReturnType<Telescope['getLogs']>;
100
+ /**
101
+ * Get storage statistics.
102
+ */
103
+ getStats(): ReturnType<Telescope['getStats']>;
104
+ /**
105
+ * Add a WebSocket client for real-time updates.
106
+ */
107
+ addWsClient(ws: WebSocket): void;
108
+ /**
109
+ * Remove a WebSocket client.
110
+ */
111
+ removeWsClient(ws: WebSocket): void;
112
+ /**
113
+ * Broadcast an event to all connected WebSocket clients.
114
+ */
115
+ broadcast(event: StudioEvent): void;
116
+ /**
117
+ * Check if a path should be ignored for request recording.
118
+ */
119
+ shouldIgnore(path: string): boolean;
120
+ /**
121
+ * Manually prune old monitoring entries.
122
+ */
123
+ prune(olderThan: Date): Promise<number>;
124
+ /**
125
+ * Clean up resources.
126
+ */
127
+ destroy(): void;
128
+ private normalizeOptions;
129
+ }
130
+ //#endregion
131
+ export { ColumnInfo, ColumnType, CursorConfig, DataBrowserOptions, Direction, FilterCondition, FilterOperator, InMemoryMonitoringStorage, MonitoringOptions, MonitoringStorage, NormalizedStudioOptions, QueryOptions, QueryResult, SchemaInfo, SortConfig, Studio, StudioEvent, StudioEventType, StudioOptions, TableCursorConfig, TableInfo };
132
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1,132 @@
1
+ import { ColumnInfo, ColumnType, CursorConfig, DataBrowser, DataBrowserOptions, Direction, FilterCondition, FilterOperator, MonitoringOptions, NormalizedStudioOptions, QueryOptions, QueryResult, SchemaInfo, SortConfig, StudioEvent, StudioEventType, StudioOptions, TableCursorConfig, TableInfo } from "./DataBrowser-SOcqmZb2.mjs";
2
+ import { Telescope, TelescopeStorage as MonitoringStorage } from "@geekmidas/telescope";
3
+ import { InMemoryStorage as InMemoryMonitoringStorage } from "@geekmidas/telescope/storage/memory";
4
+
5
+ //#region src/Studio.d.ts
6
+
7
+ /**
8
+ * Unified development tools dashboard combining monitoring and database browsing.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { Studio, Direction } from '@geekmidas/studio';
13
+ * import { InMemoryStorage } from '@geekmidas/telescope/storage/memory';
14
+ *
15
+ * const studio = new Studio({
16
+ * monitoring: {
17
+ * storage: new InMemoryStorage(),
18
+ * },
19
+ * data: {
20
+ * db: kyselyInstance,
21
+ * cursor: { field: 'id', direction: Direction.Desc },
22
+ * },
23
+ * });
24
+ * ```
25
+ */
26
+ declare class Studio<DB = unknown> {
27
+ private telescope;
28
+ private dataBrowser;
29
+ private options;
30
+ private wsClients;
31
+ constructor(options: StudioOptions<DB>);
32
+ /**
33
+ * Get the Studio dashboard path.
34
+ */
35
+ get path(): string;
36
+ /**
37
+ * Check if Studio is enabled.
38
+ */
39
+ get enabled(): boolean;
40
+ /**
41
+ * Get the data browser instance.
42
+ */
43
+ get data(): DataBrowser<DB>;
44
+ /**
45
+ * Check if body recording is enabled for monitoring.
46
+ */
47
+ get recordBody(): boolean;
48
+ /**
49
+ * Get max body size for monitoring.
50
+ */
51
+ get maxBodySize(): number;
52
+ /**
53
+ * Record a request entry.
54
+ */
55
+ recordRequest(entry: Parameters<Telescope['recordRequest']>[0]): Promise<string>;
56
+ /**
57
+ * Record log entries in batch.
58
+ */
59
+ log(entries: Parameters<Telescope['log']>[0]): Promise<void>;
60
+ /**
61
+ * Log a debug message.
62
+ */
63
+ debug(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
64
+ /**
65
+ * Log an info message.
66
+ */
67
+ info(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
68
+ /**
69
+ * Log a warning message.
70
+ */
71
+ warn(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
72
+ /**
73
+ * Log an error message.
74
+ */
75
+ error(message: string, context?: Record<string, unknown>, requestId?: string): Promise<void>;
76
+ /**
77
+ * Record an exception.
78
+ */
79
+ exception(error: Error, requestId?: string): Promise<void>;
80
+ /**
81
+ * Get requests from storage.
82
+ */
83
+ getRequests(options?: Parameters<Telescope['getRequests']>[0]): ReturnType<Telescope['getRequests']>;
84
+ /**
85
+ * Get a single request by ID.
86
+ */
87
+ getRequest(id: string): ReturnType<Telescope['getRequest']>;
88
+ /**
89
+ * Get exceptions from storage.
90
+ */
91
+ getExceptions(options?: Parameters<Telescope['getExceptions']>[0]): ReturnType<Telescope['getExceptions']>;
92
+ /**
93
+ * Get a single exception by ID.
94
+ */
95
+ getException(id: string): ReturnType<Telescope['getException']>;
96
+ /**
97
+ * Get logs from storage.
98
+ */
99
+ getLogs(options?: Parameters<Telescope['getLogs']>[0]): ReturnType<Telescope['getLogs']>;
100
+ /**
101
+ * Get storage statistics.
102
+ */
103
+ getStats(): ReturnType<Telescope['getStats']>;
104
+ /**
105
+ * Add a WebSocket client for real-time updates.
106
+ */
107
+ addWsClient(ws: WebSocket): void;
108
+ /**
109
+ * Remove a WebSocket client.
110
+ */
111
+ removeWsClient(ws: WebSocket): void;
112
+ /**
113
+ * Broadcast an event to all connected WebSocket clients.
114
+ */
115
+ broadcast(event: StudioEvent): void;
116
+ /**
117
+ * Check if a path should be ignored for request recording.
118
+ */
119
+ shouldIgnore(path: string): boolean;
120
+ /**
121
+ * Manually prune old monitoring entries.
122
+ */
123
+ prune(olderThan: Date): Promise<number>;
124
+ /**
125
+ * Clean up resources.
126
+ */
127
+ destroy(): void;
128
+ private normalizeOptions;
129
+ }
130
+ //#endregion
131
+ export { ColumnInfo, ColumnType, CursorConfig, DataBrowserOptions, Direction, FilterCondition, FilterOperator, InMemoryMonitoringStorage, MonitoringOptions, MonitoringStorage, NormalizedStudioOptions, QueryOptions, QueryResult, SchemaInfo, SortConfig, Studio, StudioEvent, StudioEventType, StudioOptions, TableCursorConfig, TableInfo };
132
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,230 @@
1
+ import { Direction, FilterOperator } from "./types-BZv87Ikv.mjs";
2
+ import { DataBrowser } from "./DataBrowser-DQ3-ZxdV.mjs";
3
+ import { Telescope } from "@geekmidas/telescope";
4
+ import { InMemoryStorage as InMemoryMonitoringStorage } from "@geekmidas/telescope/storage/memory";
5
+
6
+ //#region src/Studio.ts
7
+ /**
8
+ * Unified development tools dashboard combining monitoring and database browsing.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * import { Studio, Direction } from '@geekmidas/studio';
13
+ * import { InMemoryStorage } from '@geekmidas/telescope/storage/memory';
14
+ *
15
+ * const studio = new Studio({
16
+ * monitoring: {
17
+ * storage: new InMemoryStorage(),
18
+ * },
19
+ * data: {
20
+ * db: kyselyInstance,
21
+ * cursor: { field: 'id', direction: Direction.Desc },
22
+ * },
23
+ * });
24
+ * ```
25
+ */
26
+ var Studio = class {
27
+ telescope;
28
+ dataBrowser;
29
+ options;
30
+ wsClients = /* @__PURE__ */ new Set();
31
+ constructor(options) {
32
+ this.options = this.normalizeOptions(options);
33
+ this.telescope = new Telescope({
34
+ storage: this.options.monitoring.storage,
35
+ enabled: this.options.enabled,
36
+ path: `${this.options.path}/monitoring`,
37
+ recordBody: this.options.monitoring.recordBody,
38
+ maxBodySize: this.options.monitoring.maxBodySize,
39
+ ignorePatterns: [...this.options.monitoring.ignorePatterns, `${this.options.path}/*`],
40
+ pruneAfterHours: this.options.monitoring.pruneAfterHours
41
+ });
42
+ this.dataBrowser = new DataBrowser(this.options.data);
43
+ }
44
+ /**
45
+ * Get the Studio dashboard path.
46
+ */
47
+ get path() {
48
+ return this.options.path;
49
+ }
50
+ /**
51
+ * Check if Studio is enabled.
52
+ */
53
+ get enabled() {
54
+ return this.options.enabled;
55
+ }
56
+ /**
57
+ * Get the data browser instance.
58
+ */
59
+ get data() {
60
+ return this.dataBrowser;
61
+ }
62
+ /**
63
+ * Check if body recording is enabled for monitoring.
64
+ */
65
+ get recordBody() {
66
+ return this.options.monitoring.recordBody;
67
+ }
68
+ /**
69
+ * Get max body size for monitoring.
70
+ */
71
+ get maxBodySize() {
72
+ return this.options.monitoring.maxBodySize;
73
+ }
74
+ /**
75
+ * Record a request entry.
76
+ */
77
+ async recordRequest(entry) {
78
+ return this.telescope.recordRequest(entry);
79
+ }
80
+ /**
81
+ * Record log entries in batch.
82
+ */
83
+ async log(entries) {
84
+ return this.telescope.log(entries);
85
+ }
86
+ /**
87
+ * Log a debug message.
88
+ */
89
+ async debug(message, context, requestId) {
90
+ return this.telescope.debug(message, context, requestId);
91
+ }
92
+ /**
93
+ * Log an info message.
94
+ */
95
+ async info(message, context, requestId) {
96
+ return this.telescope.info(message, context, requestId);
97
+ }
98
+ /**
99
+ * Log a warning message.
100
+ */
101
+ async warn(message, context, requestId) {
102
+ return this.telescope.warn(message, context, requestId);
103
+ }
104
+ /**
105
+ * Log an error message.
106
+ */
107
+ async error(message, context, requestId) {
108
+ return this.telescope.error(message, context, requestId);
109
+ }
110
+ /**
111
+ * Record an exception.
112
+ */
113
+ async exception(error, requestId) {
114
+ return this.telescope.exception(error, requestId);
115
+ }
116
+ /**
117
+ * Get requests from storage.
118
+ */
119
+ async getRequests(options) {
120
+ return this.telescope.getRequests(options);
121
+ }
122
+ /**
123
+ * Get a single request by ID.
124
+ */
125
+ async getRequest(id) {
126
+ return this.telescope.getRequest(id);
127
+ }
128
+ /**
129
+ * Get exceptions from storage.
130
+ */
131
+ async getExceptions(options) {
132
+ return this.telescope.getExceptions(options);
133
+ }
134
+ /**
135
+ * Get a single exception by ID.
136
+ */
137
+ async getException(id) {
138
+ return this.telescope.getException(id);
139
+ }
140
+ /**
141
+ * Get logs from storage.
142
+ */
143
+ async getLogs(options) {
144
+ return this.telescope.getLogs(options);
145
+ }
146
+ /**
147
+ * Get storage statistics.
148
+ */
149
+ async getStats() {
150
+ return this.telescope.getStats();
151
+ }
152
+ /**
153
+ * Add a WebSocket client for real-time updates.
154
+ */
155
+ addWsClient(ws) {
156
+ this.wsClients.add(ws);
157
+ this.telescope.addWsClient(ws);
158
+ }
159
+ /**
160
+ * Remove a WebSocket client.
161
+ */
162
+ removeWsClient(ws) {
163
+ this.wsClients.delete(ws);
164
+ this.telescope.removeWsClient(ws);
165
+ }
166
+ /**
167
+ * Broadcast an event to all connected WebSocket clients.
168
+ */
169
+ broadcast(event) {
170
+ const data = JSON.stringify(event);
171
+ for (const client of this.wsClients) try {
172
+ client.send(data);
173
+ } catch {
174
+ this.wsClients.delete(client);
175
+ }
176
+ }
177
+ /**
178
+ * Check if a path should be ignored for request recording.
179
+ */
180
+ shouldIgnore(path) {
181
+ if (path.startsWith(this.options.path)) return true;
182
+ return this.telescope.shouldIgnore(path);
183
+ }
184
+ /**
185
+ * Manually prune old monitoring entries.
186
+ */
187
+ async prune(olderThan) {
188
+ return this.telescope.prune(olderThan);
189
+ }
190
+ /**
191
+ * Clean up resources.
192
+ */
193
+ destroy() {
194
+ this.telescope.destroy();
195
+ this.wsClients.clear();
196
+ }
197
+ normalizeOptions(options) {
198
+ const path = options.path ?? "/__studio";
199
+ return {
200
+ monitoring: {
201
+ storage: options.monitoring.storage,
202
+ ignorePatterns: options.monitoring.ignorePatterns ?? [],
203
+ recordBody: options.monitoring.recordBody ?? true,
204
+ maxBodySize: options.monitoring.maxBodySize ?? 64 * 1024,
205
+ pruneAfterHours: options.monitoring.pruneAfterHours
206
+ },
207
+ data: {
208
+ db: options.data.db,
209
+ cursor: options.data.cursor,
210
+ tableCursors: options.data.tableCursors ?? {},
211
+ excludeTables: options.data.excludeTables ?? [
212
+ "kysely_migration",
213
+ "kysely_migration_lock",
214
+ "_prisma_migrations",
215
+ "schema_migrations",
216
+ "_migrations",
217
+ "migrations"
218
+ ],
219
+ defaultPageSize: Math.min(options.data.defaultPageSize ?? 50, 100),
220
+ showBinaryColumns: options.data.showBinaryColumns ?? false
221
+ },
222
+ path,
223
+ enabled: options.enabled ?? true
224
+ };
225
+ }
226
+ };
227
+
228
+ //#endregion
229
+ export { Direction, FilterOperator, InMemoryMonitoringStorage, Studio };
230
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["options: StudioOptions<DB>","entry: Parameters<Telescope['recordRequest']>[0]","entries: Parameters<Telescope['log']>[0]","message: string","context?: Record<string, unknown>","requestId?: string","error: Error","options?: Parameters<Telescope['getRequests']>[0]","id: string","options?: Parameters<Telescope['getExceptions']>[0]","options?: Parameters<Telescope['getLogs']>[0]","ws: WebSocket","event: StudioEvent","path: string","olderThan: Date"],"sources":["../src/Studio.ts"],"sourcesContent":["import { Telescope } from '@geekmidas/telescope';\nimport { DataBrowser } from './data/DataBrowser';\nimport type {\n NormalizedStudioOptions,\n StudioEvent,\n StudioOptions,\n} from './types';\n\n/**\n * Unified development tools dashboard combining monitoring and database browsing.\n *\n * @example\n * ```typescript\n * import { Studio, Direction } from '@geekmidas/studio';\n * import { InMemoryStorage } from '@geekmidas/telescope/storage/memory';\n *\n * const studio = new Studio({\n * monitoring: {\n * storage: new InMemoryStorage(),\n * },\n * data: {\n * db: kyselyInstance,\n * cursor: { field: 'id', direction: Direction.Desc },\n * },\n * });\n * ```\n */\nexport class Studio<DB = unknown> {\n private telescope: Telescope;\n private dataBrowser: DataBrowser<DB>;\n private options: NormalizedStudioOptions<DB>;\n private wsClients = new Set<WebSocket>();\n\n constructor(options: StudioOptions<DB>) {\n this.options = this.normalizeOptions(options);\n\n // Initialize Telescope internally\n this.telescope = new Telescope({\n storage: this.options.monitoring.storage,\n enabled: this.options.enabled,\n path: `${this.options.path}/monitoring`,\n recordBody: this.options.monitoring.recordBody,\n maxBodySize: this.options.monitoring.maxBodySize,\n ignorePatterns: [\n ...this.options.monitoring.ignorePatterns,\n `${this.options.path}/*`, // Ignore Studio's own routes\n ],\n pruneAfterHours: this.options.monitoring.pruneAfterHours,\n });\n\n // Initialize DataBrowser\n this.dataBrowser = new DataBrowser(this.options.data);\n }\n\n // ============================================\n // Public API - Configuration\n // ============================================\n\n /**\n * Get the Studio dashboard path.\n */\n get path(): string {\n return this.options.path;\n }\n\n /**\n * Check if Studio is enabled.\n */\n get enabled(): boolean {\n return this.options.enabled;\n }\n\n /**\n * Get the data browser instance.\n */\n get data(): DataBrowser<DB> {\n return this.dataBrowser;\n }\n\n /**\n * Check if body recording is enabled for monitoring.\n */\n get recordBody(): boolean {\n return this.options.monitoring.recordBody;\n }\n\n /**\n * Get max body size for monitoring.\n */\n get maxBodySize(): number {\n return this.options.monitoring.maxBodySize;\n }\n\n // ============================================\n // Public API - Monitoring (delegated to Telescope)\n // ============================================\n\n /**\n * Record a request entry.\n */\n async recordRequest(\n entry: Parameters<Telescope['recordRequest']>[0],\n ): Promise<string> {\n return this.telescope.recordRequest(entry);\n }\n\n /**\n * Record log entries in batch.\n */\n async log(entries: Parameters<Telescope['log']>[0]): Promise<void> {\n return this.telescope.log(entries);\n }\n\n /**\n * Log a debug message.\n */\n async debug(\n message: string,\n context?: Record<string, unknown>,\n requestId?: string,\n ): Promise<void> {\n return this.telescope.debug(message, context, requestId);\n }\n\n /**\n * Log an info message.\n */\n async info(\n message: string,\n context?: Record<string, unknown>,\n requestId?: string,\n ): Promise<void> {\n return this.telescope.info(message, context, requestId);\n }\n\n /**\n * Log a warning message.\n */\n async warn(\n message: string,\n context?: Record<string, unknown>,\n requestId?: string,\n ): Promise<void> {\n return this.telescope.warn(message, context, requestId);\n }\n\n /**\n * Log an error message.\n */\n async error(\n message: string,\n context?: Record<string, unknown>,\n requestId?: string,\n ): Promise<void> {\n return this.telescope.error(message, context, requestId);\n }\n\n /**\n * Record an exception.\n */\n async exception(error: Error, requestId?: string): Promise<void> {\n return this.telescope.exception(error, requestId);\n }\n\n /**\n * Get requests from storage.\n */\n async getRequests(\n options?: Parameters<Telescope['getRequests']>[0],\n ): ReturnType<Telescope['getRequests']> {\n return this.telescope.getRequests(options);\n }\n\n /**\n * Get a single request by ID.\n */\n async getRequest(id: string): ReturnType<Telescope['getRequest']> {\n return this.telescope.getRequest(id);\n }\n\n /**\n * Get exceptions from storage.\n */\n async getExceptions(\n options?: Parameters<Telescope['getExceptions']>[0],\n ): ReturnType<Telescope['getExceptions']> {\n return this.telescope.getExceptions(options);\n }\n\n /**\n * Get a single exception by ID.\n */\n async getException(id: string): ReturnType<Telescope['getException']> {\n return this.telescope.getException(id);\n }\n\n /**\n * Get logs from storage.\n */\n async getLogs(\n options?: Parameters<Telescope['getLogs']>[0],\n ): ReturnType<Telescope['getLogs']> {\n return this.telescope.getLogs(options);\n }\n\n /**\n * Get storage statistics.\n */\n async getStats(): ReturnType<Telescope['getStats']> {\n return this.telescope.getStats();\n }\n\n // ============================================\n // Public API - WebSocket\n // ============================================\n\n /**\n * Add a WebSocket client for real-time updates.\n */\n addWsClient(ws: WebSocket): void {\n this.wsClients.add(ws);\n // Also add to Telescope for monitoring events\n this.telescope.addWsClient(ws);\n }\n\n /**\n * Remove a WebSocket client.\n */\n removeWsClient(ws: WebSocket): void {\n this.wsClients.delete(ws);\n this.telescope.removeWsClient(ws);\n }\n\n /**\n * Broadcast an event to all connected WebSocket clients.\n */\n broadcast(event: StudioEvent): void {\n const data = JSON.stringify(event);\n for (const client of this.wsClients) {\n try {\n client.send(data);\n } catch {\n this.wsClients.delete(client);\n }\n }\n }\n\n // ============================================\n // Public API - Lifecycle\n // ============================================\n\n /**\n * Check if a path should be ignored for request recording.\n */\n shouldIgnore(path: string): boolean {\n // Ignore Studio's own routes\n if (path.startsWith(this.options.path)) {\n return true;\n }\n return this.telescope.shouldIgnore(path);\n }\n\n /**\n * Manually prune old monitoring entries.\n */\n async prune(olderThan: Date): Promise<number> {\n return this.telescope.prune(olderThan);\n }\n\n /**\n * Clean up resources.\n */\n destroy(): void {\n this.telescope.destroy();\n this.wsClients.clear();\n }\n\n // ============================================\n // Private Methods\n // ============================================\n\n private normalizeOptions(\n options: StudioOptions<DB>,\n ): NormalizedStudioOptions<DB> {\n const path = options.path ?? '/__studio';\n\n return {\n monitoring: {\n storage: options.monitoring.storage,\n ignorePatterns: options.monitoring.ignorePatterns ?? [],\n recordBody: options.monitoring.recordBody ?? true,\n maxBodySize: options.monitoring.maxBodySize ?? 64 * 1024,\n pruneAfterHours: options.monitoring.pruneAfterHours,\n },\n data: {\n db: options.data.db,\n cursor: options.data.cursor,\n tableCursors: options.data.tableCursors ?? {},\n excludeTables: options.data.excludeTables ?? [\n // Kysely\n 'kysely_migration',\n 'kysely_migration_lock',\n // Prisma\n '_prisma_migrations',\n // Rails/Knex\n 'schema_migrations',\n // Generic\n '_migrations',\n 'migrations',\n ],\n defaultPageSize: Math.min(options.data.defaultPageSize ?? 50, 100),\n showBinaryColumns: options.data.showBinaryColumns ?? false,\n },\n path,\n enabled: options.enabled ?? true,\n };\n }\n}\n"],"mappings":"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"}