@geekmidas/studio 0.2.0 → 1.0.0

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 (77) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/{DataBrowser-hGwiTffZ.d.cts → DataBrowser-B-jz8KBR.d.mts} +5 -2
  3. package/dist/DataBrowser-B-jz8KBR.d.mts.map +1 -0
  4. package/dist/{DataBrowser-SOcqmZb2.d.mts → DataBrowser-BTe9HWJy.d.cts} +5 -2
  5. package/dist/DataBrowser-BTe9HWJy.d.cts.map +1 -0
  6. package/dist/{DataBrowser-c-Gs6PZB.cjs → DataBrowser-D8c_pBf4.cjs} +4 -4
  7. package/dist/DataBrowser-D8c_pBf4.cjs.map +1 -0
  8. package/dist/{DataBrowser-DQ3-ZxdV.mjs → DataBrowser-kgcI9ApJ.mjs} +4 -4
  9. package/dist/DataBrowser-kgcI9ApJ.mjs.map +1 -0
  10. package/dist/Studio-CYzz3wD2.d.cts +152 -0
  11. package/dist/Studio-CYzz3wD2.d.cts.map +1 -0
  12. package/dist/Studio-D5yGscb8.d.mts +152 -0
  13. package/dist/Studio-D5yGscb8.d.mts.map +1 -0
  14. package/dist/data/index.cjs +1 -1
  15. package/dist/data/index.d.cts +1 -1
  16. package/dist/data/index.d.mts +1 -1
  17. package/dist/data/index.mjs +1 -1
  18. package/dist/index.cjs +33 -3
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +4 -131
  21. package/dist/index.d.mts +4 -131
  22. package/dist/index.mjs +33 -3
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/server/hono.cjs +168 -21
  25. package/dist/server/hono.cjs.map +1 -1
  26. package/dist/server/hono.d.cts +13 -2
  27. package/dist/server/hono.d.cts.map +1 -0
  28. package/dist/server/hono.d.mts +13 -2
  29. package/dist/server/hono.d.mts.map +1 -0
  30. package/dist/server/hono.mjs +168 -21
  31. package/dist/server/hono.mjs.map +1 -1
  32. package/dist/types-BZv87Ikv.mjs.map +1 -1
  33. package/dist/types-CMttUZYk.cjs.map +1 -1
  34. package/package.json +5 -5
  35. package/src/Studio.ts +341 -292
  36. package/src/__tests__/Studio.spec.ts +447 -0
  37. package/src/data/DataBrowser.ts +147 -143
  38. package/src/data/__tests__/DataBrowser.integration.spec.ts +404 -404
  39. package/src/data/__tests__/filtering.integration.spec.ts +726 -726
  40. package/src/data/__tests__/introspection.integration.spec.ts +340 -340
  41. package/src/data/__tests__/pagination.spec.ts +123 -0
  42. package/src/data/filtering.ts +154 -154
  43. package/src/data/introspection.ts +141 -141
  44. package/src/data/pagination.ts +15 -15
  45. package/src/index.ts +22 -24
  46. package/src/server/__tests__/hono.integration.spec.ts +605 -347
  47. package/src/server/hono.ts +392 -190
  48. package/src/types.ts +138 -138
  49. package/src/ui-assets.ts +10 -13
  50. package/tsconfig.json +9 -0
  51. package/tsdown.config.ts +9 -9
  52. package/ui/CHANGELOG.md +12 -0
  53. package/ui/package.json +28 -22
  54. package/ui/src/App.tsx +95 -235
  55. package/ui/src/api.ts +184 -42
  56. package/ui/src/components/FilterPanel.tsx +198 -198
  57. package/ui/src/components/NavRail.tsx +183 -0
  58. package/ui/src/components/RowDetail.tsx +106 -106
  59. package/ui/src/components/StudioHeader.tsx +109 -0
  60. package/ui/src/components/TableList.tsx +49 -49
  61. package/ui/src/components/TableView.tsx +530 -485
  62. package/ui/src/main.tsx +3 -3
  63. package/ui/src/pages/DashboardPage.tsx +500 -0
  64. package/ui/src/pages/DatabasePage.tsx +226 -0
  65. package/ui/src/pages/EndpointDetailsPage.tsx +288 -0
  66. package/ui/src/pages/ExceptionsPage.tsx +268 -0
  67. package/ui/src/pages/LogsPage.tsx +228 -0
  68. package/ui/src/pages/MonitoringPage.tsx +46 -0
  69. package/ui/src/pages/PerformancePage.tsx +307 -0
  70. package/ui/src/pages/RequestsPage.tsx +379 -0
  71. package/ui/src/providers/StudioProvider.tsx +194 -0
  72. package/ui/src/styles.css +53 -142
  73. package/ui/src/types.ts +154 -30
  74. package/ui/tsconfig.tsbuildinfo +1 -1
  75. package/ui/vite.config.ts +6 -6
  76. package/dist/DataBrowser-DQ3-ZxdV.mjs.map +0 -1
  77. package/dist/DataBrowser-c-Gs6PZB.cjs.map +0 -1
@@ -1,12 +1,12 @@
1
1
  import type { Kysely, SelectQueryBuilder } from 'kysely';
2
2
  import {
3
- type CursorConfig,
4
- type DataBrowserOptions,
5
- Direction,
6
- type QueryOptions,
7
- type QueryResult,
8
- type SchemaInfo,
9
- type TableInfo,
3
+ type CursorConfig,
4
+ type DataBrowserOptions,
5
+ Direction,
6
+ type QueryOptions,
7
+ type QueryResult,
8
+ type SchemaInfo,
9
+ type TableInfo,
10
10
  } from '../types';
11
11
  import { applyFilters, applySorting } from './filtering';
12
12
  import { introspectSchema } from './introspection';
@@ -27,140 +27,144 @@ import { decodeCursor, encodeCursor } from './pagination';
27
27
  * ```
28
28
  */
29
29
  export class DataBrowser<DB = unknown> {
30
- private db: Kysely<DB>;
31
- private options: Required<DataBrowserOptions<DB>>;
32
- private schemaCache: SchemaInfo | null = null;
33
- private schemaCacheExpiry = 0;
34
- private readonly CACHE_TTL_MS = 60 * 1000; // 1 minute
35
-
36
- constructor(options: Required<DataBrowserOptions<DB>>) {
37
- this.db = options.db;
38
- this.options = options;
39
- }
40
-
41
- // ============================================
42
- // Schema Introspection
43
- // ============================================
44
-
45
- /**
46
- * Get the database schema information.
47
- * Results are cached for 1 minute.
48
- *
49
- * @param forceRefresh - Force a refresh of the cache
50
- */
51
- async getSchema(forceRefresh = false): Promise<SchemaInfo> {
52
- const now = Date.now();
53
-
54
- if (!forceRefresh && this.schemaCache && now < this.schemaCacheExpiry) {
55
- return this.schemaCache;
56
- }
57
-
58
- const schema = await introspectSchema(this.db, this.options.excludeTables);
59
-
60
- this.schemaCache = schema;
61
- this.schemaCacheExpiry = now + this.CACHE_TTL_MS;
62
-
63
- return schema;
64
- }
65
-
66
- /**
67
- * Get information about a specific table.
68
- */
69
- async getTableInfo(tableName: string): Promise<TableInfo | null> {
70
- const schema = await this.getSchema();
71
- return schema.tables.find((t) => t.name === tableName) ?? null;
72
- }
73
-
74
- // ============================================
75
- // Data Querying
76
- // ============================================
77
-
78
- /**
79
- * Query table data with pagination, filtering, and sorting.
80
- */
81
- async query(options: QueryOptions): Promise<QueryResult> {
82
- const tableInfo = await this.getTableInfo(options.table);
83
-
84
- if (!tableInfo) {
85
- throw new Error(`Table '${options.table}' not found`);
86
- }
87
-
88
- const cursorConfig = this.getCursorConfig(options.table);
89
- const pageSize = Math.min(
90
- options.pageSize ?? this.options.defaultPageSize,
91
- 100,
92
- );
93
-
94
- // Build base query selecting all columns
95
- let query = this.db
96
- .selectFrom(options.table as any)
97
- .selectAll() as SelectQueryBuilder<any, any, any>;
98
-
99
- // Apply filters if provided
100
- if (options.filters && options.filters.length > 0) {
101
- query = applyFilters(query, options.filters, tableInfo);
102
- }
103
-
104
- // Apply sorting if provided, otherwise use cursor field
105
- if (options.sort && options.sort.length > 0) {
106
- query = applySorting(query, options.sort, tableInfo);
107
- } else {
108
- query = query.orderBy(cursorConfig.field as any, cursorConfig.direction);
109
- }
110
-
111
- // Handle cursor-based pagination
112
- if (options.cursor) {
113
- const cursorValue = decodeCursor(options.cursor);
114
- const operator = cursorConfig.direction === Direction.Asc ? '>' : '<';
115
- query = query.where(cursorConfig.field as any, operator, cursorValue);
116
- }
117
-
118
- // Fetch one extra row to determine if there are more results
119
- const rows = await query.limit(pageSize + 1).execute();
120
-
121
- const hasMore = rows.length > pageSize;
122
- const resultRows = hasMore ? rows.slice(0, pageSize) : rows;
123
-
124
- // Generate cursors
125
- let nextCursor: string | null = null;
126
- let prevCursor: string | null = null;
127
-
128
- if (hasMore && resultRows.length > 0) {
129
- const lastRow = resultRows[resultRows.length - 1];
130
- nextCursor = encodeCursor(lastRow[cursorConfig.field]);
131
- }
132
-
133
- // For prev cursor, we need to know if there are previous results
134
- // This would require a separate query, so we'll only set it if there's an input cursor
135
- if (options.cursor && resultRows.length > 0) {
136
- const firstRow = resultRows[0];
137
- prevCursor = encodeCursor(firstRow[cursorConfig.field]);
138
- }
139
-
140
- return {
141
- rows: resultRows,
142
- hasMore,
143
- nextCursor,
144
- prevCursor,
145
- };
146
- }
147
-
148
- // ============================================
149
- // Configuration Access
150
- // ============================================
151
-
152
- /**
153
- * Get the cursor configuration for a table.
154
- * Returns the table-specific config if defined, otherwise the default.
155
- */
156
- getCursorConfig(tableName: string): CursorConfig {
157
- return this.options.tableCursors[tableName] ?? this.options.cursor;
158
- }
159
-
160
- /**
161
- * Get the underlying Kysely database instance.
162
- */
163
- get database(): Kysely<DB> {
164
- return this.db;
165
- }
30
+ private db: Kysely<DB>;
31
+ private options: Required<DataBrowserOptions<DB>>;
32
+ private schemaCache: SchemaInfo | null = null;
33
+ private schemaCacheExpiry = 0;
34
+ private readonly CACHE_TTL_MS = 60 * 1000; // 1 minute
35
+
36
+ constructor(options: Required<DataBrowserOptions<DB>>) {
37
+ this.db = options.db;
38
+ this.options = options;
39
+ }
40
+
41
+ // ============================================
42
+ // Schema Introspection
43
+ // ============================================
44
+
45
+ /**
46
+ * Get the database schema information.
47
+ * Results are cached for 1 minute.
48
+ *
49
+ * @param forceRefresh - Force a refresh of the cache
50
+ */
51
+ async getSchema(forceRefresh = false): Promise<SchemaInfo> {
52
+ const now = Date.now();
53
+
54
+ if (!forceRefresh && this.schemaCache && now < this.schemaCacheExpiry) {
55
+ return this.schemaCache;
56
+ }
57
+
58
+ const schema = await introspectSchema(this.db, this.options.excludeTables);
59
+
60
+ this.schemaCache = schema;
61
+ this.schemaCacheExpiry = now + this.CACHE_TTL_MS;
62
+
63
+ return schema;
64
+ }
65
+
66
+ /**
67
+ * Get information about a specific table.
68
+ */
69
+ async getTableInfo(tableName: string): Promise<TableInfo | null> {
70
+ const schema = await this.getSchema();
71
+ return schema.tables.find((t) => t.name === tableName) ?? null;
72
+ }
73
+
74
+ // ============================================
75
+ // Data Querying
76
+ // ============================================
77
+
78
+ /**
79
+ * Query table data with pagination, filtering, and sorting.
80
+ */
81
+ async query(options: QueryOptions): Promise<QueryResult> {
82
+ const tableInfo = await this.getTableInfo(options.table);
83
+
84
+ if (!tableInfo) {
85
+ throw new Error(`Table '${options.table}' not found`);
86
+ }
87
+
88
+ const cursorConfig = this.getCursorConfig(options.table);
89
+ const pageSize = Math.min(
90
+ options.pageSize ?? this.options.defaultPageSize,
91
+ 100,
92
+ );
93
+
94
+ // Build base query selecting all columns
95
+ let query = this.db
96
+ .selectFrom(options.table as any)
97
+ .selectAll() as SelectQueryBuilder<any, any, any>;
98
+
99
+ // Apply filters if provided
100
+ if (options.filters && options.filters.length > 0) {
101
+ query = applyFilters(query, options.filters, tableInfo);
102
+ }
103
+
104
+ // Apply sorting if provided, otherwise use cursor field
105
+ if (options.sort && options.sort.length > 0) {
106
+ query = applySorting(query, options.sort, tableInfo);
107
+ } else {
108
+ query = query.orderBy(cursorConfig.field as any, cursorConfig.direction);
109
+ }
110
+
111
+ // Handle cursor-based pagination
112
+ if (options.cursor) {
113
+ const cursorValue = decodeCursor(options.cursor);
114
+ const operator = cursorConfig.direction === Direction.Asc ? '>' : '<';
115
+ query = query.where(cursorConfig.field as any, operator, cursorValue);
116
+ }
117
+
118
+ // Fetch one extra row to determine if there are more results
119
+ const rows = await query.limit(pageSize + 1).execute();
120
+
121
+ const hasMore = rows.length > pageSize;
122
+ const resultRows = hasMore ? rows.slice(0, pageSize) : rows;
123
+
124
+ // Generate cursors
125
+ let nextCursor: string | null = null;
126
+ let prevCursor: string | null = null;
127
+
128
+ if (hasMore && resultRows.length > 0) {
129
+ const lastRow = resultRows[resultRows.length - 1];
130
+ if (lastRow) {
131
+ nextCursor = encodeCursor(lastRow[cursorConfig.field]);
132
+ }
133
+ }
134
+
135
+ // For prev cursor, we need to know if there are previous results
136
+ // This would require a separate query, so we'll only set it if there's an input cursor
137
+ if (options.cursor && resultRows.length > 0) {
138
+ const firstRow = resultRows[0];
139
+ if (firstRow) {
140
+ prevCursor = encodeCursor(firstRow[cursorConfig.field]);
141
+ }
142
+ }
143
+
144
+ return {
145
+ rows: resultRows,
146
+ hasMore,
147
+ nextCursor,
148
+ prevCursor,
149
+ };
150
+ }
151
+
152
+ // ============================================
153
+ // Configuration Access
154
+ // ============================================
155
+
156
+ /**
157
+ * Get the cursor configuration for a table.
158
+ * Returns the table-specific config if defined, otherwise the default.
159
+ */
160
+ getCursorConfig(tableName: string): CursorConfig {
161
+ return this.options.tableCursors[tableName] ?? this.options.cursor;
162
+ }
163
+
164
+ /**
165
+ * Get the underlying Kysely database instance.
166
+ */
167
+ get database(): Kysely<DB> {
168
+ return this.db;
169
+ }
166
170
  }