@rebasepro/studio 0.11.1-canary.gfd39654 → 0.12.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.
- package/dist/{JSEditor-BeHHuxhA.js → JSEditor-CIkng40W.js} +31 -7
- package/dist/JSEditor-CIkng40W.js.map +1 -0
- package/dist/RLSEditor-rLlMcZPG.js.map +1 -1
- package/dist/{SQLEditor-_dCqVAN_.js → SQLEditor-DFYk9r1u.js} +4 -4
- package/dist/{SQLEditor-_dCqVAN_.js.map → SQLEditor-DFYk9r1u.js.map} +1 -1
- package/dist/components/RLSEditor/RLSEditor.d.ts +1 -10
- package/dist/index.es.js +3 -3
- package/dist/index.es.js.map +1 -1
- package/package.json +10 -9
- package/src/components/JSEditor/JSEditor.tsx +2 -2
- package/src/components/JSEditor/JSMonacoEditor.tsx +28 -4
- package/src/components/RLSEditor/RLSEditor.tsx +3 -10
- package/src/components/RebaseStudio.tsx +1 -1
- package/src/components/SQLEditor/SQLEditor.tsx +3 -3
- package/src/components/StudioHomePage.tsx +1 -1
- package/src/index.ts +1 -1
- package/src/utils/sql_utils.test.ts +2 -2
- package/dist/JSEditor-BeHHuxhA.js.map +0 -1
- package/dist/utils/pgColumnToProperty.d.ts +0 -7
- package/src/utils/pgColumnToProperty.test.ts +0 -402
- package/src/utils/pgColumnToProperty.ts +0 -310
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
import type { PostgresProperties, Property, StringProperty, NumberProperty, ArrayProperty, TableColumnInfo, TableMetadata, SecurityOperation, SecurityRule } from "@rebasepro/types";
|
|
2
|
-
import type { AdminCollection } from "@rebasepro/admin-types";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Maps a PostgreSQL column data type to a Rebase property type.
|
|
6
|
-
*/
|
|
7
|
-
function pgTypeToRebaseProperty(column: TableColumnInfo): Property | null {
|
|
8
|
-
const {
|
|
9
|
-
column_name,
|
|
10
|
-
data_type,
|
|
11
|
-
udt_name,
|
|
12
|
-
is_nullable,
|
|
13
|
-
column_default,
|
|
14
|
-
enum_values
|
|
15
|
-
} = column;
|
|
16
|
-
|
|
17
|
-
const required = is_nullable === "NO";
|
|
18
|
-
const prettifiedName = column_name
|
|
19
|
-
.replace(/_/g, " ")
|
|
20
|
-
.replace(/\b\w/g, (c: string) => c.toUpperCase());
|
|
21
|
-
|
|
22
|
-
// Detect if this column is a primary key (auto-generated id)
|
|
23
|
-
const isAutoId = column_default != null && (
|
|
24
|
-
column_default.includes("nextval") ||
|
|
25
|
-
column_default.includes("gen_random_uuid") ||
|
|
26
|
-
column_default.includes("uuid_generate") ||
|
|
27
|
-
column_default.includes("identity")
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
// USER-DEFINED = PostgreSQL enums
|
|
31
|
-
if (data_type === "USER-DEFINED" && enum_values && enum_values.length > 0) {
|
|
32
|
-
return {
|
|
33
|
-
type: "string",
|
|
34
|
-
name: prettifiedName,
|
|
35
|
-
enum: enum_values.map((v: string) => ({ id: v,
|
|
36
|
-
label: v.replace(/_/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase()) })),
|
|
37
|
-
validation: required ? { required: true } : undefined
|
|
38
|
-
} as StringProperty;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const dt = data_type.toLowerCase();
|
|
42
|
-
switch (dt) {
|
|
43
|
-
case "character varying":
|
|
44
|
-
case "varchar":
|
|
45
|
-
case "text":
|
|
46
|
-
case "char":
|
|
47
|
-
case "character":
|
|
48
|
-
case "citext": {
|
|
49
|
-
let colType: "varchar" | "text" | "char" = "varchar";
|
|
50
|
-
if (dt === "text" || dt === "citext") colType = "text";
|
|
51
|
-
if (dt === "char" || dt === "character") colType = "char";
|
|
52
|
-
const prop: StringProperty = {
|
|
53
|
-
type: "string",
|
|
54
|
-
name: prettifiedName,
|
|
55
|
-
columnType: colType,
|
|
56
|
-
validation: required ? { required: true } : undefined
|
|
57
|
-
};
|
|
58
|
-
if (isAutoId) {
|
|
59
|
-
prop.isId = "manual";
|
|
60
|
-
}
|
|
61
|
-
return prop;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
case "uuid": {
|
|
65
|
-
const prop: StringProperty = {
|
|
66
|
-
type: "string",
|
|
67
|
-
name: prettifiedName,
|
|
68
|
-
validation: required ? { required: true } : undefined
|
|
69
|
-
};
|
|
70
|
-
if (isAutoId) {
|
|
71
|
-
prop.isId = "uuid";
|
|
72
|
-
}
|
|
73
|
-
return prop;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
case "integer":
|
|
77
|
-
case "bigint":
|
|
78
|
-
case "smallint": {
|
|
79
|
-
const colType = dt === "bigint" ? "bigint" : "integer";
|
|
80
|
-
const prop: NumberProperty = {
|
|
81
|
-
type: "number",
|
|
82
|
-
name: prettifiedName,
|
|
83
|
-
columnType: colType,
|
|
84
|
-
validation: {
|
|
85
|
-
...(required ? { required: true } : {}),
|
|
86
|
-
integer: true
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
if (isAutoId) {
|
|
90
|
-
prop.isId = "increment";
|
|
91
|
-
}
|
|
92
|
-
return prop;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
case "serial":
|
|
96
|
-
case "bigserial":
|
|
97
|
-
case "smallserial": {
|
|
98
|
-
const colType = dt === "bigserial" ? "bigserial" : "serial";
|
|
99
|
-
return {
|
|
100
|
-
type: "number",
|
|
101
|
-
name: prettifiedName,
|
|
102
|
-
columnType: colType,
|
|
103
|
-
isId: "increment",
|
|
104
|
-
validation: {
|
|
105
|
-
...(required ? { required: true } : {}),
|
|
106
|
-
integer: true
|
|
107
|
-
}
|
|
108
|
-
} as NumberProperty;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
case "numeric":
|
|
112
|
-
case "decimal":
|
|
113
|
-
case "real":
|
|
114
|
-
case "double precision": {
|
|
115
|
-
let colType: "numeric" | "real" | "double precision" = "numeric";
|
|
116
|
-
if (dt === "real") colType = "real";
|
|
117
|
-
if (dt === "double precision") colType = "double precision";
|
|
118
|
-
return {
|
|
119
|
-
type: "number",
|
|
120
|
-
name: prettifiedName,
|
|
121
|
-
columnType: colType,
|
|
122
|
-
validation: required ? { required: true } : undefined
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
case "boolean":
|
|
127
|
-
return {
|
|
128
|
-
type: "boolean",
|
|
129
|
-
name: prettifiedName,
|
|
130
|
-
validation: required ? { required: true } : undefined
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
case "timestamp with time zone":
|
|
134
|
-
case "timestamp without time zone":
|
|
135
|
-
case "timestamp":
|
|
136
|
-
case "timestamptz":
|
|
137
|
-
case "date":
|
|
138
|
-
case "time with time zone":
|
|
139
|
-
case "time without time zone":
|
|
140
|
-
case "time": {
|
|
141
|
-
let colType: "timestamp" | "date" | "time" = "timestamp";
|
|
142
|
-
if (dt.startsWith("date")) colType = "date";
|
|
143
|
-
if (dt.startsWith("time ") || dt === "time") colType = "time";
|
|
144
|
-
return {
|
|
145
|
-
type: "date",
|
|
146
|
-
name: prettifiedName,
|
|
147
|
-
columnType: colType,
|
|
148
|
-
validation: required ? { required: true } : undefined
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
case "jsonb":
|
|
153
|
-
case "json":
|
|
154
|
-
return {
|
|
155
|
-
type: "map",
|
|
156
|
-
name: prettifiedName,
|
|
157
|
-
columnType: dt === "jsonb" ? "jsonb" : "json",
|
|
158
|
-
keyValue: true,
|
|
159
|
-
properties: {}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
case "array":
|
|
163
|
-
case "ARRAY": {
|
|
164
|
-
let innerType = "string";
|
|
165
|
-
let colType: ArrayProperty["columnType"] = undefined;
|
|
166
|
-
if (udt_name === "_text" || udt_name === "_varchar") {
|
|
167
|
-
innerType = "string";
|
|
168
|
-
colType = "text[]";
|
|
169
|
-
} else if (udt_name === "_int4" || udt_name === "_int2" || udt_name === "_int8") {
|
|
170
|
-
innerType = "number";
|
|
171
|
-
colType = "integer[]";
|
|
172
|
-
} else if (udt_name === "_bool") {
|
|
173
|
-
innerType = "boolean";
|
|
174
|
-
colType = "boolean[]";
|
|
175
|
-
} else if (udt_name === "_numeric") {
|
|
176
|
-
innerType = "number";
|
|
177
|
-
colType = "numeric[]";
|
|
178
|
-
}
|
|
179
|
-
return {
|
|
180
|
-
type: "array",
|
|
181
|
-
name: prettifiedName,
|
|
182
|
-
columnType: colType,
|
|
183
|
-
of: { type: innerType }
|
|
184
|
-
} as ArrayProperty;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
default:
|
|
188
|
-
// Fallback: treat unknown types as string
|
|
189
|
-
return {
|
|
190
|
-
type: "string",
|
|
191
|
-
name: prettifiedName,
|
|
192
|
-
validation: required ? { required: true } : undefined
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Builds a partial AdminCollection from PostgreSQL table metadata.
|
|
199
|
-
* This is used when creating a new collection from an existing database table.
|
|
200
|
-
*/
|
|
201
|
-
export function buildCollectionFromTableMetadata(
|
|
202
|
-
tableName: string,
|
|
203
|
-
metadata: TableMetadata
|
|
204
|
-
): Partial<AdminCollection> {
|
|
205
|
-
const properties: Record<string, Property> = {};
|
|
206
|
-
const propertiesOrder: string[] = [];
|
|
207
|
-
// Introspection can only ever produce two shapes: a foreign key on this
|
|
208
|
-
// table, or a junction between two. Both are named by their kind.
|
|
209
|
-
const relations: Array<{
|
|
210
|
-
id: string;
|
|
211
|
-
relationName: string;
|
|
212
|
-
target: string;
|
|
213
|
-
kind: "belongsTo" | "manyToMany";
|
|
214
|
-
localKey?: string;
|
|
215
|
-
through?: { table: string; sourceColumn: string; targetColumn: string };
|
|
216
|
-
}> = [];
|
|
217
|
-
const securityRules: SecurityRule[] = [];
|
|
218
|
-
|
|
219
|
-
// Parse columns
|
|
220
|
-
for (const column of metadata.columns) {
|
|
221
|
-
const property = pgTypeToRebaseProperty(column);
|
|
222
|
-
if (property) {
|
|
223
|
-
const propRecord = property as unknown as Record<string, unknown>;
|
|
224
|
-
Object.keys(propRecord).forEach(key => propRecord[key] === undefined && delete propRecord[key]);
|
|
225
|
-
|
|
226
|
-
properties[column.column_name] = property;
|
|
227
|
-
propertiesOrder.push(column.column_name);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Parse Outgoing Foreign Keys -> Many-to-One / One-to-One
|
|
232
|
-
if (metadata.foreignKeys) {
|
|
233
|
-
for (const fk of metadata.foreignKeys) {
|
|
234
|
-
const relName = fk.column_name.endsWith("_id") ? fk.column_name.substring(0, fk.column_name.length - 3) : fk.column_name;
|
|
235
|
-
relations.push({
|
|
236
|
-
id: fk.column_name,
|
|
237
|
-
relationName: relName,
|
|
238
|
-
target: fk.foreign_table_name, // Will be hydrated later
|
|
239
|
-
kind: "belongsTo",
|
|
240
|
-
localKey: fk.column_name
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Parse Incoming Junctions -> Many-to-Many
|
|
246
|
-
if (metadata.junctions) {
|
|
247
|
-
for (const junction of metadata.junctions) {
|
|
248
|
-
const relName = junction.target_table_name; // E.g., 'roles'
|
|
249
|
-
relations.push({
|
|
250
|
-
id: junction.target_table_name + "_relation",
|
|
251
|
-
relationName: relName,
|
|
252
|
-
target: junction.target_table_name, // Will be hydrated later
|
|
253
|
-
kind: "manyToMany",
|
|
254
|
-
through: {
|
|
255
|
-
table: junction.junction_table_name,
|
|
256
|
-
sourceColumn: junction.source_column_name,
|
|
257
|
-
targetColumn: junction.target_column_name
|
|
258
|
-
}
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// Parse RLS Policies
|
|
264
|
-
if (metadata.policies) {
|
|
265
|
-
for (const policy of metadata.policies) {
|
|
266
|
-
// Attempt to map typical cmds to operations.
|
|
267
|
-
// Postgres cmd: SELECT, INSERT, UPDATE, DELETE, ALL
|
|
268
|
-
let operations: SecurityOperation[] = [];
|
|
269
|
-
switch (policy.cmd) {
|
|
270
|
-
case "ALL": operations = ["all"]; break;
|
|
271
|
-
case "SELECT": operations = ["select"]; break;
|
|
272
|
-
case "INSERT": operations = ["insert"]; break;
|
|
273
|
-
case "UPDATE": operations = ["update"]; break;
|
|
274
|
-
case "DELETE": operations = ["delete"]; break;
|
|
275
|
-
}
|
|
276
|
-
const qual = policy.qual ?? undefined;
|
|
277
|
-
const withCheck = policy.with_check ?? undefined;
|
|
278
|
-
if (qual) {
|
|
279
|
-
securityRules.push({
|
|
280
|
-
name: policy.policy_name,
|
|
281
|
-
operations,
|
|
282
|
-
roles: policy.roles ?? [],
|
|
283
|
-
using: qual,
|
|
284
|
-
...(withCheck ? { withCheck } : {})
|
|
285
|
-
});
|
|
286
|
-
} else {
|
|
287
|
-
securityRules.push({
|
|
288
|
-
name: policy.policy_name,
|
|
289
|
-
operations,
|
|
290
|
-
roles: policy.roles ?? []
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const prettifiedName = tableName
|
|
297
|
-
.replace(/_/g, " ")
|
|
298
|
-
.replace(/\b\w/g, (c: string) => c.toUpperCase());
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
name: prettifiedName,
|
|
302
|
-
slug: tableName,
|
|
303
|
-
table: tableName,
|
|
304
|
-
properties: properties as PostgresProperties,
|
|
305
|
-
propertiesOrder,
|
|
306
|
-
// `target` is still a slug here — the caller hydrates it into a thunk.
|
|
307
|
-
...(relations.length > 0 ? { relations: relations as unknown as AdminCollection["relations"] } : {}),
|
|
308
|
-
...(securityRules.length > 0 ? { securityRules } : {})
|
|
309
|
-
};
|
|
310
|
-
}
|