@rebasepro/studio 0.4.0 → 0.5.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/README.md +73 -140
- package/dist/{JSEditor-BCSoElPg.js → JSEditor-DfwRLBZg.js} +2 -2
- package/dist/JSEditor-DfwRLBZg.js.map +1 -0
- package/dist/RLSEditor-CHEExeSB.js.map +1 -1
- package/dist/{SQLEditor-BC0IOUQu.js → SQLEditor-CQXaI0iU.js} +2 -2
- package/dist/SQLEditor-CQXaI0iU.js.map +1 -0
- package/dist/SchemaVisualizer-BGpmzyXT.js.map +1 -1
- package/dist/common/src/util/permissions.d.ts +14 -6
- package/dist/index.es.js +2 -2
- package/dist/index.umd.js +2 -2
- package/dist/index.umd.js.map +1 -1
- package/dist/studio/src/components/RLSEditor/RLSEditor.d.ts +0 -6
- package/dist/studio/src/components/SchemaVisualizer/schema-visualizer.utils.d.ts +0 -8
- package/dist/studio/src/utils/pgColumnToProperty.d.ts +1 -1
- package/dist/types/src/types/backend.d.ts +36 -1
- package/dist/types/src/types/collections.d.ts +21 -1
- package/dist/types/src/types/properties.d.ts +0 -8
- package/package.json +8 -8
- package/src/components/JSEditor/JSEditor.tsx +1 -1
- package/src/components/RLSEditor/RLSEditor.tsx +1 -1
- package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +3 -3
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +2 -2
- package/src/utils/pgColumnToProperty.ts +23 -20
- package/src/utils/sql_utils.ts +1 -1
- package/dist/JSEditor-BCSoElPg.js.map +0 -1
- package/dist/SQLEditor-BC0IOUQu.js.map +0 -1
- package/dist/studio/src/components/SchemaVisualizer/index.d.ts +0 -5
- package/dist/studio/src/utils/entities.d.ts +0 -0
- package/src/components/SchemaVisualizer/index.ts +0 -5
- package/src/utils/entities.ts +0 -2
|
@@ -8,12 +8,6 @@ export interface PostgresPolicy {
|
|
|
8
8
|
with_check: string | null;
|
|
9
9
|
status?: "live" | "code_only" | "both";
|
|
10
10
|
}
|
|
11
|
-
export interface TableRLSStatus {
|
|
12
|
-
schemaName: string;
|
|
13
|
-
tableName: string;
|
|
14
|
-
rlsEnabled: boolean;
|
|
15
|
-
policies: PostgresPolicy[];
|
|
16
|
-
}
|
|
17
11
|
export declare const RLSEditor: ({ apiUrl }: {
|
|
18
12
|
apiUrl?: string;
|
|
19
13
|
}) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import type { Node, Edge } from "@xyflow/react";
|
|
2
2
|
export declare const NODE_WIDTH = 280;
|
|
3
|
-
/** Header with a single line of text (junction tables or tableName === collectionName). */
|
|
4
|
-
export declare const HEADER_HEIGHT_SINGLE = 33;
|
|
5
|
-
/** Header with two lines (name + subtitle when collectionName !== tableName). */
|
|
6
|
-
export declare const HEADER_HEIGHT_DOUBLE = 47;
|
|
7
3
|
/**
|
|
8
4
|
* Compute the correct header height for a table node.
|
|
9
5
|
*/
|
|
@@ -12,10 +8,6 @@ export declare const getHeaderHeight: (opts: {
|
|
|
12
8
|
collectionName: string;
|
|
13
9
|
tableName: string;
|
|
14
10
|
}) => number;
|
|
15
|
-
/**
|
|
16
|
-
* Estimate the pixel height of a table node based on column count.
|
|
17
|
-
*/
|
|
18
|
-
export declare const estimateNodeHeight: (columnCount: number, headerHeight?: number) => number;
|
|
19
11
|
/**
|
|
20
12
|
* Get the vertical center Y of a specific column row (0-indexed)
|
|
21
13
|
* relative to the top of the node.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityCollection, TableMetadata } from "@rebasepro/types";
|
|
1
|
+
import type { EntityCollection, TableMetadata } from "@rebasepro/types";
|
|
2
2
|
/**
|
|
3
3
|
* Builds a partial EntityCollection from PostgreSQL table metadata.
|
|
4
4
|
* This is used when creating a new collection from an existing database table.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Entity } from "./entities";
|
|
2
2
|
import type { EntityCollection, FilterValues, WhereFilterOp } from "./collections";
|
|
3
|
+
import type { AuthAdapter } from "./auth_adapter";
|
|
3
4
|
/**
|
|
4
5
|
* Abstract database connection interface.
|
|
5
6
|
* Represents a connection to any database system.
|
|
@@ -182,6 +183,24 @@ export interface RealtimeProvider {
|
|
|
182
183
|
* Notify all relevant subscribers of an entity update
|
|
183
184
|
*/
|
|
184
185
|
notifyEntityUpdate(path: string, entityId: string, entity: Entity | null, databaseId?: string): Promise<void>;
|
|
186
|
+
/**
|
|
187
|
+
* Called when the HTTP server is ready and listening.
|
|
188
|
+
* Useful for providers that need the server address for callbacks.
|
|
189
|
+
*/
|
|
190
|
+
onServerReady?(serverInfo: {
|
|
191
|
+
port: number;
|
|
192
|
+
hostname?: string;
|
|
193
|
+
}): void;
|
|
194
|
+
/**
|
|
195
|
+
* Gracefully shut down the realtime provider.
|
|
196
|
+
* Called during server shutdown to clean up resources.
|
|
197
|
+
*/
|
|
198
|
+
destroy?(): Promise<void>;
|
|
199
|
+
/**
|
|
200
|
+
* Stop the internal LISTEN client (e.g., PostgreSQL LISTEN/NOTIFY).
|
|
201
|
+
* Called during graceful shutdown before closing database connections.
|
|
202
|
+
*/
|
|
203
|
+
stopListening?(): Promise<void>;
|
|
185
204
|
}
|
|
186
205
|
/**
|
|
187
206
|
* Abstract collection registry interface.
|
|
@@ -464,6 +483,22 @@ export interface BackendBootstrapper {
|
|
|
464
483
|
* (e.g., `"postgres"`, `"mongodb"`, `"mysql"`).
|
|
465
484
|
*/
|
|
466
485
|
type: string;
|
|
486
|
+
/**
|
|
487
|
+
* Unique identifier for this bootstrapper instance.
|
|
488
|
+
* Used to register the driver in the driver registry.
|
|
489
|
+
* Defaults to `type` if not set.
|
|
490
|
+
*/
|
|
491
|
+
id?: string;
|
|
492
|
+
/**
|
|
493
|
+
* Whether this bootstrapper provides the default driver.
|
|
494
|
+
* When true, the coordinator uses this driver as the primary one.
|
|
495
|
+
*/
|
|
496
|
+
isDefault?: boolean;
|
|
497
|
+
/**
|
|
498
|
+
* Run database migrations for this driver.
|
|
499
|
+
* Called by the coordinator after all drivers are initialized.
|
|
500
|
+
*/
|
|
501
|
+
runMigrations?(config: unknown, driverResult: InitializedDriver): Promise<void>;
|
|
467
502
|
/**
|
|
468
503
|
* Create a DataDriver from the given config.
|
|
469
504
|
* This is the only **required** method.
|
|
@@ -498,7 +533,7 @@ export interface BackendBootstrapper {
|
|
|
498
533
|
/**
|
|
499
534
|
* Initialize WebSocket server for realtime operations.
|
|
500
535
|
*/
|
|
501
|
-
initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import("../controllers/data_driver").DataDriver, config?: unknown): Promise<void> | void;
|
|
536
|
+
initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import("../controllers/data_driver").DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> | void;
|
|
502
537
|
}
|
|
503
538
|
/**
|
|
504
539
|
* Result of `BackendBootstrapper.initializeDriver()`.
|
|
@@ -343,6 +343,23 @@ export interface BaseEntityCollection<M extends Record<string, unknown> = Record
|
|
|
343
343
|
* Builder for the collection actions rendered in the toolbar
|
|
344
344
|
*/
|
|
345
345
|
Actions?: ComponentRef<CollectionActionsProps>[];
|
|
346
|
+
/**
|
|
347
|
+
* The database table name for this collection.
|
|
348
|
+
* Automatically set for PostgreSQL collections.
|
|
349
|
+
* For non-SQL backends, this may be undefined.
|
|
350
|
+
*/
|
|
351
|
+
table?: string;
|
|
352
|
+
/**
|
|
353
|
+
* Relations defined for this collection.
|
|
354
|
+
* Populated at normalization time from inline relation properties
|
|
355
|
+
* or explicit relation definitions.
|
|
356
|
+
*/
|
|
357
|
+
relations?: Relation[];
|
|
358
|
+
/**
|
|
359
|
+
* Security rules for this collection (Row Level Security).
|
|
360
|
+
* When defined, the backend enforces access control policies.
|
|
361
|
+
*/
|
|
362
|
+
securityRules?: SecurityRule[];
|
|
346
363
|
}
|
|
347
364
|
/**
|
|
348
365
|
* A collection backed by PostgreSQL (or any SQL database).
|
|
@@ -436,7 +453,10 @@ export interface MongoDBCollection<M extends Record<string, unknown> = Record<st
|
|
|
436
453
|
* @group Models
|
|
437
454
|
*/
|
|
438
455
|
export type EntityCollection<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> = PostgresCollection<M, USER> | FirebaseCollection<M, USER> | MongoDBCollection<M, USER>;
|
|
439
|
-
/**
|
|
456
|
+
/**
|
|
457
|
+
* An EntityCollection that supports SQL-style relations (e.g. Postgres).
|
|
458
|
+
* @deprecated Use `EntityCollection` directly — `table`, `relations`, and `securityRules` are now on `BaseEntityCollection`.
|
|
459
|
+
*/
|
|
440
460
|
export type CollectionWithRelations<M extends Record<string, unknown> = Record<string, unknown>> = EntityCollection<M> & {
|
|
441
461
|
table?: string;
|
|
442
462
|
relations?: Relation[];
|
|
@@ -641,14 +641,6 @@ export interface MapProperty extends BaseProperty {
|
|
|
641
641
|
* Properties that are displayed when rendered as a preview
|
|
642
642
|
*/
|
|
643
643
|
previewProperties?: string[];
|
|
644
|
-
/**
|
|
645
|
-
* Allow the user to add only some keys in this map.
|
|
646
|
-
* By default, all properties of the map have the corresponding field in
|
|
647
|
-
* the form view. Setting this flag to true allows to pick only some.
|
|
648
|
-
* Useful for map that can have a lot of sub-properties that may not be
|
|
649
|
-
* needed
|
|
650
|
-
*/
|
|
651
|
-
pickOnlySomeKeys?: boolean;
|
|
652
644
|
/**
|
|
653
645
|
* Render this map as a key-value table that allows to use
|
|
654
646
|
* arbitrary keys. You don't need to define the properties in this case.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/studio",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"main": "./dist/index.umd.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
7
7
|
"types": "dist/studio/src/index.d.ts",
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
"pgsql-ast-parser": "12.0.2",
|
|
16
16
|
"prism-react-renderer": "^2.4.1",
|
|
17
17
|
"react-dropzone": "^14.4.1",
|
|
18
|
-
"@rebasepro/client": "0.
|
|
19
|
-
"@rebasepro/common": "0.
|
|
20
|
-
"@rebasepro/
|
|
21
|
-
"@rebasepro/
|
|
22
|
-
"@rebasepro/
|
|
23
|
-
"@rebasepro/
|
|
18
|
+
"@rebasepro/client": "0.5.0",
|
|
19
|
+
"@rebasepro/common": "0.5.0",
|
|
20
|
+
"@rebasepro/utils": "0.5.0",
|
|
21
|
+
"@rebasepro/core": "0.5.0",
|
|
22
|
+
"@rebasepro/types": "0.5.0",
|
|
23
|
+
"@rebasepro/ui": "0.5.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"react": ">=19.0.0",
|
|
27
27
|
"react-dom": ">=19.0.0",
|
|
28
28
|
"react-router": "^7.0.0",
|
|
29
29
|
"react-router-dom": "^7.0.0",
|
|
30
|
-
"@rebasepro/admin": "0.
|
|
30
|
+
"@rebasepro/admin": "0.5.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependenciesMeta": {
|
|
33
33
|
"@rebasepro/admin": {
|
|
@@ -166,7 +166,7 @@ function detectCollectionsInResult(
|
|
|
166
166
|
for (const slug of mentionedSlugs) {
|
|
167
167
|
const normalised = toSnakeCase(slug);
|
|
168
168
|
const col = collections.find(c => {
|
|
169
|
-
const tableName = (
|
|
169
|
+
const tableName = ("table" in c ? c.table : undefined) || toSnakeCase(c.slug);
|
|
170
170
|
return c.slug === slug || tableName === normalised || toSnakeCase(c.slug) === normalised;
|
|
171
171
|
});
|
|
172
172
|
if (col) {
|
|
@@ -4,9 +4,9 @@ import type { Node, Edge } from "@xyflow/react";
|
|
|
4
4
|
// ─── Layout Constants ─────────────────────────────────────────────────
|
|
5
5
|
export const NODE_WIDTH = 280;
|
|
6
6
|
/** Header with a single line of text (junction tables or tableName === collectionName). */
|
|
7
|
-
|
|
7
|
+
const HEADER_HEIGHT_SINGLE = 33;
|
|
8
8
|
/** Header with two lines (name + subtitle when collectionName !== tableName). */
|
|
9
|
-
|
|
9
|
+
const HEADER_HEIGHT_DOUBLE = 47;
|
|
10
10
|
const ROW_HEIGHT = 28; // height per column row
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -26,7 +26,7 @@ export const getHeaderHeight = (opts: {
|
|
|
26
26
|
/**
|
|
27
27
|
* Estimate the pixel height of a table node based on column count.
|
|
28
28
|
*/
|
|
29
|
-
|
|
29
|
+
const estimateNodeHeight = (columnCount: number, headerHeight: number = HEADER_HEIGHT_DOUBLE): number =>
|
|
30
30
|
headerHeight + Math.max(columnCount, 1) * ROW_HEIGHT + 4; // +4 for bottom padding
|
|
31
31
|
|
|
32
32
|
/**
|
|
@@ -46,9 +46,9 @@ const extractColumns = (collection: EntityCollection): ColumnInfo[] => {
|
|
|
46
46
|
if (prop.type === "relation") continue; // Relations are shown as edges, not columns
|
|
47
47
|
|
|
48
48
|
const isPk =
|
|
49
|
-
("isId" in prop && Boolean(
|
|
49
|
+
("isId" in prop && Boolean(prop.isId)) ||
|
|
50
50
|
(!Object.values(properties).some(
|
|
51
|
-
(p) => "isId" in p && Boolean(
|
|
51
|
+
(p) => "isId" in p && Boolean(p.isId)
|
|
52
52
|
) &&
|
|
53
53
|
propName === "id");
|
|
54
54
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { EntityCollection, Property, StringProperty, NumberProperty, ArrayProperty, TableColumnInfo, TableMetadata } from "@rebasepro/types";
|
|
1
|
+
import type { EntityCollection, Property, StringProperty, NumberProperty, ArrayProperty, TableColumnInfo, TableMetadata, SecurityOperation, SecurityRule } from "@rebasepro/types";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Maps a PostgreSQL column data type to a Rebase property type.
|
|
@@ -212,13 +212,7 @@ export function buildCollectionFromTableMetadata(
|
|
|
212
212
|
localKey?: string;
|
|
213
213
|
through?: { table: string; sourceColumn: string; targetColumn: string };
|
|
214
214
|
}> = [];
|
|
215
|
-
const securityRules:
|
|
216
|
-
name: string;
|
|
217
|
-
operations: string[];
|
|
218
|
-
roles: string[];
|
|
219
|
-
qual: string | null | undefined;
|
|
220
|
-
with_check: string | null | undefined;
|
|
221
|
-
}> = [];
|
|
215
|
+
const securityRules: SecurityRule[] = [];
|
|
222
216
|
|
|
223
217
|
// Parse columns
|
|
224
218
|
for (const column of metadata.columns) {
|
|
@@ -271,22 +265,31 @@ export function buildCollectionFromTableMetadata(
|
|
|
271
265
|
for (const policy of metadata.policies) {
|
|
272
266
|
// Attempt to map typical cmds to operations.
|
|
273
267
|
// Postgres cmd: SELECT, INSERT, UPDATE, DELETE, ALL
|
|
274
|
-
let operations:
|
|
268
|
+
let operations: SecurityOperation[] = [];
|
|
275
269
|
switch (policy.cmd) {
|
|
276
|
-
case "ALL": operations = ["
|
|
277
|
-
case "SELECT": operations = ["
|
|
278
|
-
case "INSERT": operations = ["
|
|
270
|
+
case "ALL": operations = ["all"]; break;
|
|
271
|
+
case "SELECT": operations = ["select"]; break;
|
|
272
|
+
case "INSERT": operations = ["insert"]; break;
|
|
279
273
|
case "UPDATE": operations = ["update"]; break;
|
|
280
274
|
case "DELETE": operations = ["delete"]; break;
|
|
281
275
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
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
|
+
}
|
|
290
293
|
}
|
|
291
294
|
}
|
|
292
295
|
|
package/src/utils/sql_utils.ts
CHANGED
|
@@ -105,7 +105,7 @@ export function resolveQueryCollections(
|
|
|
105
105
|
for (const table of tables) {
|
|
106
106
|
// Match table name against collection table or slug->snake_case
|
|
107
107
|
const matched = collections.find(c => {
|
|
108
|
-
const tableName = (
|
|
108
|
+
const tableName = ("table" in c ? c.table : undefined) || toSnakeCase(c.slug);
|
|
109
109
|
return tableName === table.name;
|
|
110
110
|
});
|
|
111
111
|
|