@rebasepro/studio 0.17.3 → 0.18.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/LICENSE +0 -1
- package/README.md +12 -4
- package/dist/{ApiKeysView-DiCurTEU.js → ApiKeysView-EIvegrUk.js} +54 -39
- package/dist/ApiKeysView-EIvegrUk.js.map +1 -0
- package/dist/{BackupsView-DNS6LdVg.js → BackupsView-DVkKzaPR.js} +29 -31
- package/dist/BackupsView-DVkKzaPR.js.map +1 -0
- package/dist/{BranchesView-CM3B-ER3.js → BranchesView-Df8hQ8wE.js} +85 -24
- package/dist/BranchesView-Df8hQ8wE.js.map +1 -0
- package/dist/{CronJobsView-BHtJZeJZ.js → CronJobsView-CLOGjb-L.js} +32 -27
- package/dist/CronJobsView-CLOGjb-L.js.map +1 -0
- package/dist/{RLSEditor-CBX4uD66.js → RLSEditor-Sa1D34ny.js} +724 -615
- package/dist/RLSEditor-Sa1D34ny.js.map +1 -0
- package/dist/{SQLEditor-ByecwX8B.js → SQLEditor-BfNA8xM3.js} +9 -4
- package/dist/{SQLEditor-ByecwX8B.js.map → SQLEditor-BfNA8xM3.js.map} +1 -1
- package/dist/{SchemaVisualizer-D6tMZgeC.js → SchemaVisualizer-DTDMYL6o.js} +84 -110
- package/dist/SchemaVisualizer-DTDMYL6o.js.map +1 -0
- package/dist/{StorageView-BGDeC5GI.js → StorageView-Ed9Q_VlB.js} +5 -32
- package/dist/StorageView-Ed9Q_VlB.js.map +1 -0
- package/dist/components/RLSEditor/saveRules.d.ts +38 -0
- package/dist/components/RebaseStudio.d.ts +1 -1
- package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +5 -2
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +0 -4
- package/dist/components/load-failure-view.d.ts +17 -0
- package/dist/components/load-failure.d.ts +47 -0
- package/dist/index.es.js +56 -36
- package/dist/index.es.js.map +1 -1
- package/dist/load-failure-C6OMJSGC.js +31 -0
- package/dist/load-failure-C6OMJSGC.js.map +1 -0
- package/dist/load-failure-view-CBTB_h-h.js +48 -0
- package/dist/load-failure-view-CBTB_h-h.js.map +1 -0
- package/package.json +36 -21
- package/dist/ApiKeysView-DiCurTEU.js.map +0 -1
- package/dist/BackupsView-DNS6LdVg.js.map +0 -1
- package/dist/BranchesView-CM3B-ER3.js.map +0 -1
- package/dist/CronJobsView-BHtJZeJZ.js.map +0 -1
- package/dist/RLSEditor-CBX4uD66.js.map +0 -1
- package/dist/SchemaVisualizer-D6tMZgeC.js.map +0 -1
- package/dist/StorageView-BGDeC5GI.js.map +0 -1
- package/dist/components/StorageView/storage-failure.d.ts +0 -34
- package/src/components/ApiExplorer/ApiExplorer.tsx +0 -299
- package/src/components/ApiExplorer/EndpointDetail.tsx +0 -279
- package/src/components/ApiExplorer/TryItPanel.tsx +0 -499
- package/src/components/ApiExplorer/parseSpec.ts +0 -104
- package/src/components/ApiExplorer/types.ts +0 -84
- package/src/components/ApiKeys/ApiKeysView.tsx +0 -479
- package/src/components/ApiKeys/CreateApiKeyDialog.tsx +0 -711
- package/src/components/ApiKeys/permissions.ts +0 -145
- package/src/components/AuthSimulationSelector.tsx +0 -72
- package/src/components/Backups/BackupsView.tsx +0 -185
- package/src/components/Branches/BranchesView.tsx +0 -386
- package/src/components/CronJobs/CronJobsView.tsx +0 -413
- package/src/components/JSEditor/JSEditor.tsx +0 -1100
- package/src/components/JSEditor/JSEditorSidebar.tsx +0 -350
- package/src/components/JSEditor/JSMonacoEditor.tsx +0 -410
- package/src/components/LogsExplorer/LogsExplorer.tsx +0 -273
- package/src/components/LogsExplorer/useLogTail.ts +0 -356
- package/src/components/RLSEditor/PolicyEditor.tsx +0 -399
- package/src/components/RLSEditor/RLSEditor.tsx +0 -1128
- package/src/components/RLSEditor/index.ts +0 -1
- package/src/components/RLSEditor/policy-presets.ts +0 -176
- package/src/components/RLSEditor/saveSecurityRules.ts +0 -48
- package/src/components/RebaseStudio.tsx +0 -174
- package/src/components/SQLEditor/ExplainVisualizer.tsx +0 -128
- package/src/components/SQLEditor/MonacoEditor.tsx +0 -203
- package/src/components/SQLEditor/SQLEditor.tsx +0 -1518
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +0 -173
- package/src/components/SQLEditor/SchemaBrowser.tsx +0 -168
- package/src/components/SQLEditor/sql_editor_types.ts +0 -18
- package/src/components/SchemaVisualizer/RelationEdge.tsx +0 -102
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +0 -710
- package/src/components/SchemaVisualizer/TableNode.tsx +0 -257
- package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +0 -140
- package/src/components/SchemaVisualizer/useLiveRls.ts +0 -86
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +0 -426
- package/src/components/StorageView/StorageView.tsx +0 -1588
- package/src/components/StorageView/storage-failure.ts +0 -63
- package/src/components/StudioHomePage.tsx +0 -430
- package/src/index.ts +0 -31
- package/src/utils/parseSpec.test.ts +0 -309
- package/src/utils/sql_utils.test.ts +0 -265
- package/src/utils/sql_utils.ts +0 -291
package/src/utils/sql_utils.ts
DELETED
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import { parseFirst } from "pgsql-ast-parser";
|
|
2
|
-
import type { TableInfo } from "../components/SQLEditor/sql_editor_types";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* A table extracted from a SQL query's FROM/JOIN clauses.
|
|
6
|
-
*/
|
|
7
|
-
export interface ExtractedTable {
|
|
8
|
-
name: string;
|
|
9
|
-
alias?: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* A collection matched to a table in a SQL query.
|
|
14
|
-
*/
|
|
15
|
-
export interface ResolvedQueryCollection {
|
|
16
|
-
/** DB table name from the SQL AST (e.g. "blog_posts") */
|
|
17
|
-
tableName: string;
|
|
18
|
-
/** SQL alias if present (e.g. "bp") */
|
|
19
|
-
tableAlias?: string;
|
|
20
|
-
/** The matched collection */
|
|
21
|
-
collection: AdminCollection;
|
|
22
|
-
/** Columns from this table that are present in the result set */
|
|
23
|
-
columns: string[];
|
|
24
|
-
/** The result column name that holds the primary key for this table (e.g. "id", "author_id") */
|
|
25
|
-
pkColumn?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Extract all tables referenced in a SQL query's FROM and JOIN clauses.
|
|
30
|
-
* Returns an empty array for non-SELECT queries or parse failures.
|
|
31
|
-
*/
|
|
32
|
-
export function extractTablesFromQuery(sqlString: string): ExtractedTable[] {
|
|
33
|
-
try {
|
|
34
|
-
const ast = parseFirst(sqlString);
|
|
35
|
-
if (ast.type !== "select") return [];
|
|
36
|
-
|
|
37
|
-
const tables: ExtractedTable[] = [];
|
|
38
|
-
|
|
39
|
-
// pgsql-ast-parser From items — tables and joins with left/right branches
|
|
40
|
-
type FromNode = { type: string; name?: { name: string; alias?: string }; left?: FromNode; right?: FromNode };
|
|
41
|
-
const processFrom = (fromItems: FromNode[]) => {
|
|
42
|
-
for (const item of fromItems) {
|
|
43
|
-
if (item.type === "table" && item.name) {
|
|
44
|
-
tables.push({ name: item.name.name,
|
|
45
|
-
alias: item.name.alias });
|
|
46
|
-
}
|
|
47
|
-
if (item.type === "join") {
|
|
48
|
-
if (item.left) processFrom([item.left]);
|
|
49
|
-
if (item.right) processFrom([item.right]);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
if (ast.from) {
|
|
55
|
-
processFrom(ast.from);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return tables;
|
|
59
|
-
} catch {
|
|
60
|
-
return [];
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
import { toSnakeCase } from "@rebasepro/utils";
|
|
65
|
-
import type { AdminCollection } from "@rebasepro/cms-types";
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Resolve which collections are referenced by a SQL query.
|
|
69
|
-
*
|
|
70
|
-
* Parses the SQL, extracts table names, and matches each against
|
|
71
|
-
* registered collections via `collection.table` (falling back to
|
|
72
|
-
* snake_case of `collection.slug`).
|
|
73
|
-
*
|
|
74
|
-
* For each matched collection, determines which result columns
|
|
75
|
-
* belong to that table using the database schema information.
|
|
76
|
-
*/
|
|
77
|
-
export function resolveQueryCollections(
|
|
78
|
-
sqlString: string,
|
|
79
|
-
schemas: Record<string, TableInfo[]>,
|
|
80
|
-
collections: AdminCollection[],
|
|
81
|
-
resultColumns?: string[]
|
|
82
|
-
): ResolvedQueryCollection[] {
|
|
83
|
-
const tables = extractTablesFromQuery(sqlString);
|
|
84
|
-
if (tables.length === 0) return [];
|
|
85
|
-
|
|
86
|
-
// Parse the AST to resolve SELECT column aliases
|
|
87
|
-
const selectColumns: { table?: string; column: string; alias?: string }[] = [];
|
|
88
|
-
try {
|
|
89
|
-
const ast = parseFirst(sqlString);
|
|
90
|
-
if (ast.type === "select" && ast.columns) {
|
|
91
|
-
for (const col of ast.columns) {
|
|
92
|
-
if (col.expr?.type === "ref") {
|
|
93
|
-
selectColumns.push({
|
|
94
|
-
table: col.expr.table?.name,
|
|
95
|
-
column: col.expr.name,
|
|
96
|
-
alias: col.alias?.name
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
} catch { /* parse failure is ok, we'll fall back */ }
|
|
102
|
-
|
|
103
|
-
const results: ResolvedQueryCollection[] = [];
|
|
104
|
-
|
|
105
|
-
for (const table of tables) {
|
|
106
|
-
// Match table name against collection table or slug->snake_case
|
|
107
|
-
const matched = collections.find(c => {
|
|
108
|
-
const tableName = ("table" in c ? c.table : undefined) || toSnakeCase(c.slug);
|
|
109
|
-
return tableName === table.name;
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
if (!matched) continue;
|
|
113
|
-
|
|
114
|
-
// Find columns belonging to this table from the schema
|
|
115
|
-
const tableColumns: string[] = [];
|
|
116
|
-
for (const schemaEntries of Object.values(schemas)) {
|
|
117
|
-
const tableInfo = schemaEntries.find(t => t.tableName === table.name);
|
|
118
|
-
if (tableInfo) {
|
|
119
|
-
tableColumns.push(...tableInfo.columns.map(c => c.name));
|
|
120
|
-
break;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Determine which result column holds the PK ("id") for this table.
|
|
125
|
-
// 1. Check parsed SELECT columns for an explicit "id" column from this table (by name or alias)
|
|
126
|
-
// 2. Fall back to checking if result columns contain "id"
|
|
127
|
-
let pkColumn: string | undefined;
|
|
128
|
-
|
|
129
|
-
// Look in the AST select columns for `table.id` or `alias.id`
|
|
130
|
-
const tableRef = table.alias || table.name;
|
|
131
|
-
const idSelectCol = selectColumns.find(
|
|
132
|
-
sc => sc.column === "id" && (!sc.table || sc.table === tableRef || sc.table === table.name)
|
|
133
|
-
);
|
|
134
|
-
if (idSelectCol) {
|
|
135
|
-
pkColumn = idSelectCol.alias || idSelectCol.column; // use alias if present
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// If we didn't find it from the AST (e.g. SELECT *), check if result columns have "id"
|
|
139
|
-
if (!pkColumn && resultColumns) {
|
|
140
|
-
if (resultColumns.includes("id")) {
|
|
141
|
-
pkColumn = "id";
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// If still not found, fall back to checking tableColumns
|
|
146
|
-
if (!pkColumn && tableColumns.includes("id")) {
|
|
147
|
-
pkColumn = "id";
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
results.push({
|
|
151
|
-
tableName: table.name,
|
|
152
|
-
tableAlias: table.alias,
|
|
153
|
-
collection: matched,
|
|
154
|
-
columns: tableColumns,
|
|
155
|
-
pkColumn
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return results;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
export interface PKMapping {
|
|
163
|
-
/** The actual column name in the database table */
|
|
164
|
-
dbColumn: string;
|
|
165
|
-
/** The column name as it appears in the query result set (may be aliased) */
|
|
166
|
-
resultColumn: string;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
export interface TableAndPKResult {
|
|
170
|
-
tableName?: string;
|
|
171
|
-
primaryKeys?: PKMapping[];
|
|
172
|
-
error?: string;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
export function determineTableAndPK(sqlString: string, columnKey: string, schemas: Record<string, TableInfo[]>): TableAndPKResult {
|
|
176
|
-
try {
|
|
177
|
-
const tables = extractTablesFromQuery(sqlString);
|
|
178
|
-
|
|
179
|
-
const ast = parseFirst(sqlString);
|
|
180
|
-
if (ast.type !== "select") {
|
|
181
|
-
return { error: "Inline editing is only supported for SELECT queries." };
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (tables.length === 0) {
|
|
185
|
-
return { error: "Could not find any tables in the query." };
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Parse SELECT columns to resolve aliases
|
|
189
|
-
const selectColumns: { table?: string; column: string; alias?: string }[] = [];
|
|
190
|
-
if (ast.columns) {
|
|
191
|
-
for (const col of ast.columns) {
|
|
192
|
-
if (col.expr?.type === "ref") {
|
|
193
|
-
selectColumns.push({
|
|
194
|
-
table: col.expr.table?.name,
|
|
195
|
-
column: col.expr.name,
|
|
196
|
-
alias: col.alias?.name
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Resolve which DB column `columnKey` refers to (it might be aliased)
|
|
203
|
-
const resolvedColumn = selectColumns.find(
|
|
204
|
-
sc => (sc.alias === columnKey) || (!sc.alias && sc.column === columnKey)
|
|
205
|
-
);
|
|
206
|
-
const actualDbColumnName = resolvedColumn?.column ?? columnKey;
|
|
207
|
-
const columnTableRef = resolvedColumn?.table; // e.g. "p" or "posts"
|
|
208
|
-
|
|
209
|
-
// Resolve the table for the edited column
|
|
210
|
-
let resolvedTableName: string | null = null;
|
|
211
|
-
|
|
212
|
-
if (tables.length === 1) {
|
|
213
|
-
resolvedTableName = tables[0].name;
|
|
214
|
-
} else {
|
|
215
|
-
// If the AST tells us which table, use that
|
|
216
|
-
if (columnTableRef) {
|
|
217
|
-
const matchedTable = tables.find(
|
|
218
|
-
t => t.alias === columnTableRef || t.name === columnTableRef
|
|
219
|
-
);
|
|
220
|
-
if (matchedTable) {
|
|
221
|
-
resolvedTableName = matchedTable.name;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Otherwise, look up which schema table has this column
|
|
226
|
-
if (!resolvedTableName) {
|
|
227
|
-
const matchedTables = tables.filter(t => {
|
|
228
|
-
for (const schema of Object.values(schemas)) {
|
|
229
|
-
const tableInfo = schema.find(ti => ti.tableName === t.name);
|
|
230
|
-
if (tableInfo && tableInfo.columns.some(c => c.name === actualDbColumnName)) {
|
|
231
|
-
return true;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
return false;
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
if (matchedTables.length === 1) {
|
|
238
|
-
resolvedTableName = matchedTables[0].name;
|
|
239
|
-
} else if (matchedTables.length > 1) {
|
|
240
|
-
return { error: `Ambiguous column "${columnKey}": Found in multiple queried tables.` };
|
|
241
|
-
} else {
|
|
242
|
-
return { error: `Could not find column "${columnKey}" in the queried tables.` };
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
if (!resolvedTableName) {
|
|
248
|
-
return { error: "Could not resolve the target table." };
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Find the table's actual primary key columns from the schema
|
|
252
|
-
let pkDbColumns: string[] = [];
|
|
253
|
-
for (const schema of Object.values(schemas)) {
|
|
254
|
-
const tableInfo = schema.find(t => t.tableName === resolvedTableName);
|
|
255
|
-
if (tableInfo) {
|
|
256
|
-
pkDbColumns = tableInfo.columns
|
|
257
|
-
.filter(c => c.isPrimaryKey)
|
|
258
|
-
.map(c => c.name);
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (pkDbColumns.length === 0) {
|
|
264
|
-
return { error: `Table "${resolvedTableName}" has no primary key defined.` };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
// Find the table's alias in the query (for resolving PK result column names)
|
|
268
|
-
const tableEntry = tables.find(t => t.name === resolvedTableName);
|
|
269
|
-
const tableAlias = tableEntry?.alias;
|
|
270
|
-
|
|
271
|
-
// Map each PK db column to its result column name (resolving aliases)
|
|
272
|
-
const primaryKeys: PKMapping[] = pkDbColumns.map(dbCol => {
|
|
273
|
-
// Find the SELECT column for this PK
|
|
274
|
-
const selectCol = selectColumns.find(
|
|
275
|
-
sc => sc.column === dbCol &&
|
|
276
|
-
(!sc.table || sc.table === (tableAlias || resolvedTableName))
|
|
277
|
-
);
|
|
278
|
-
return {
|
|
279
|
-
dbColumn: dbCol,
|
|
280
|
-
resultColumn: selectCol?.alias || selectCol?.column || dbCol
|
|
281
|
-
};
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
return { tableName: resolvedTableName,
|
|
285
|
-
primaryKeys };
|
|
286
|
-
} catch (e: unknown) {
|
|
287
|
-
console.warn("Failed to parse SQL AST:", e);
|
|
288
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
289
|
-
return { error: `Could not safely parse query for inline editing: ${message}` };
|
|
290
|
-
}
|
|
291
|
-
}
|