@rebasepro/studio 0.8.0 → 0.9.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 (32) hide show
  1. package/README.md +1 -1
  2. package/dist/{JSEditor-Ca4XYGRp.js → JSEditor-Sgmw1SHC.js} +25 -29
  3. package/dist/JSEditor-Sgmw1SHC.js.map +1 -0
  4. package/dist/{RLSEditor-BM64laoW.js → RLSEditor-DPhky8XW.js} +8 -18
  5. package/dist/RLSEditor-DPhky8XW.js.map +1 -0
  6. package/dist/{SQLEditor-CuAhR-zr.js → SQLEditor-CgR2hlUI.js} +5 -5
  7. package/dist/SQLEditor-CgR2hlUI.js.map +1 -0
  8. package/dist/{SchemaVisualizer-OibKoD3g.js → SchemaVisualizer-WK4ZZR5X.js} +9 -9
  9. package/dist/SchemaVisualizer-WK4ZZR5X.js.map +1 -0
  10. package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +2 -2
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.es.js +6 -6
  13. package/dist/index.umd.js +41 -55
  14. package/dist/index.umd.js.map +1 -1
  15. package/dist/utils/pgColumnToProperty.d.ts +3 -3
  16. package/dist/utils/sql_utils.d.ts +3 -3
  17. package/package.json +8 -8
  18. package/src/components/JSEditor/JSEditor.tsx +7 -7
  19. package/src/components/JSEditor/JSMonacoEditor.tsx +20 -24
  20. package/src/components/RLSEditor/RLSEditor.tsx +18 -31
  21. package/src/components/SQLEditor/SQLEditor.tsx +4 -4
  22. package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +7 -7
  23. package/src/components/SchemaVisualizer/useSchemaGraph.ts +11 -11
  24. package/src/index.ts +1 -1
  25. package/src/utils/pgColumnToProperty.ts +3 -3
  26. package/src/utils/sql_utils.test.ts +6 -6
  27. package/src/utils/sql_utils.ts +3 -3
  28. package/dist/JSEditor-Ca4XYGRp.js.map +0 -1
  29. package/dist/RLSEditor-BM64laoW.js.map +0 -1
  30. package/dist/SQLEditor-CuAhR-zr.js.map +0 -1
  31. package/dist/SchemaVisualizer-OibKoD3g.js.map +0 -1
  32. package/src/vite-env.d.ts +0 -1
@@ -1,6 +1,6 @@
1
- import type { EntityCollection, TableMetadata } from "@rebasepro/types";
1
+ import type { CollectionConfig, TableMetadata } from "@rebasepro/types";
2
2
  /**
3
- * Builds a partial EntityCollection from PostgreSQL table metadata.
3
+ * Builds a partial CollectionConfig from PostgreSQL table metadata.
4
4
  * This is used when creating a new collection from an existing database table.
5
5
  */
6
- export declare function buildCollectionFromTableMetadata(tableName: string, metadata: TableMetadata): Partial<EntityCollection>;
6
+ export declare function buildCollectionFromTableMetadata(tableName: string, metadata: TableMetadata): Partial<CollectionConfig>;
@@ -1,5 +1,5 @@
1
1
  import { TableInfo } from "../components/SQLEditor/SQLEditor";
2
- import { EntityCollection } from "@rebasepro/types";
2
+ import { CollectionConfig } from "@rebasepro/types";
3
3
  /**
4
4
  * A table extracted from a SQL query's FROM/JOIN clauses.
5
5
  */
@@ -16,7 +16,7 @@ export interface ResolvedQueryCollection {
16
16
  /** SQL alias if present (e.g. "bp") */
17
17
  tableAlias?: string;
18
18
  /** The matched collection */
19
- collection: EntityCollection;
19
+ collection: CollectionConfig;
20
20
  /** Columns from this table that are present in the result set */
21
21
  columns: string[];
22
22
  /** The result column name that holds the primary key for this table (e.g. "id", "author_id") */
@@ -37,7 +37,7 @@ export declare function extractTablesFromQuery(sqlString: string): ExtractedTabl
37
37
  * For each matched collection, determines which result columns
38
38
  * belong to that table using the database schema information.
39
39
  */
40
- export declare function resolveQueryCollections(sqlString: string, schemas: Record<string, TableInfo[]>, collections: EntityCollection[], resultColumns?: string[]): ResolvedQueryCollection[];
40
+ export declare function resolveQueryCollections(sqlString: string, schemas: Record<string, TableInfo[]>, collections: CollectionConfig[], resultColumns?: string[]): ResolvedQueryCollection[];
41
41
  export interface PKMapping {
42
42
  /** The actual column name in the database table */
43
43
  dbColumn: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/studio",
3
3
  "type": "module",
4
- "version": "0.8.0",
4
+ "version": "0.9.0",
5
5
  "main": "./dist/index.umd.js",
6
6
  "module": "./dist/index.es.js",
7
7
  "types": "./dist/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": "^15.0.0",
18
- "@rebasepro/common": "0.8.0",
19
- "@rebasepro/types": "0.8.0",
20
- "@rebasepro/core": "0.8.0",
21
- "@rebasepro/ui": "0.8.0",
22
- "@rebasepro/client": "0.8.0",
23
- "@rebasepro/utils": "0.8.0"
18
+ "@rebasepro/client": "0.9.0",
19
+ "@rebasepro/common": "0.9.0",
20
+ "@rebasepro/core": "0.9.0",
21
+ "@rebasepro/utils": "0.9.0",
22
+ "@rebasepro/ui": "0.9.0",
23
+ "@rebasepro/types": "0.9.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.8.0"
30
+ "@rebasepro/admin": "0.9.0"
31
31
  },
32
32
  "peerDependenciesMeta": {
33
33
  "@rebasepro/admin": {
@@ -35,9 +35,9 @@ import {
35
35
  VirtualTableColumn,
36
36
  XIcon
37
37
  } from "@rebasepro/ui";
38
- import { useStudioUrlController, useStudioCollectionRegistry, useStudioSideEntityController } from "@rebasepro/core";
38
+ import { useStudioUrlController, useStudioCollectionRegistry, useStudioSidePanelController } from "@rebasepro/core";
39
39
  import { useRebaseContext, useRebaseClient, useSnackbarController, useApiConfig, useTranslation, useModeController, ErrorView, SelectableUser, IconForView } from "@rebasepro/core";
40
- import { EntityCollection } from "@rebasepro/types";
40
+ import { CollectionConfig } from "@rebasepro/types";
41
41
  import { createRebaseClient } from "@rebasepro/client";
42
42
  import { JSMonacoEditor } from "./JSMonacoEditor";
43
43
  import { JSEditorSidebar, JSSnippet } from "./JSEditorSidebar";
@@ -113,7 +113,7 @@ function formatJSON(value: unknown): string {
113
113
 
114
114
  interface MatchedJSCollection {
115
115
  collectionSlug: string;
116
- collection: EntityCollection;
116
+ collection: CollectionConfig;
117
117
  pkColumn: string;
118
118
  }
119
119
 
@@ -126,7 +126,7 @@ interface MatchedJSCollection {
126
126
  function detectCollectionsInResult(
127
127
  code: string,
128
128
  resultValue: unknown,
129
- collections: EntityCollection[]
129
+ collections: CollectionConfig[]
130
130
  ): MatchedJSCollection[] {
131
131
  if (!resultValue || !collections?.length) return [];
132
132
 
@@ -190,7 +190,7 @@ export function JSEditor() {
190
190
  const apiConfig = useApiConfig();
191
191
  const snackbar = useSnackbarController();
192
192
  const collectionRegistry = useStudioCollectionRegistry();
193
- const sideEntityController = useStudioSideEntityController();
193
+ const sidePanelController = useStudioSidePanelController();
194
194
  const { t } = useTranslation();
195
195
 
196
196
  // User management for the "Run as" picker
@@ -854,7 +854,7 @@ id: String(ra.entityId) })}>
854
854
  className="text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300"
855
855
  onClick={(e) => {
856
856
  e.stopPropagation();
857
- sideEntityController.open({
857
+ sidePanelController.open({
858
858
  path: ra.collection.collectionSlug,
859
859
  entityId: ra.entityId,
860
860
  collection: ra.collection.collection,
@@ -887,7 +887,7 @@ id: String(ra.entityId) })}>
887
887
  key={ra.collection.collectionSlug}
888
888
  dense
889
889
  onClick={() => {
890
- sideEntityController.open({
890
+ sidePanelController.open({
891
891
  path: ra.collection.collectionSlug,
892
892
  entityId: ra.entityId,
893
893
  collection: ra.collection.collection,
@@ -23,11 +23,7 @@ interface MonacoTypeScriptApi {
23
23
  const REBASE_CLIENT_TYPES = `
24
24
  // ─── Rebase Client SDK Type Definitions ─────────────────────────────
25
25
 
26
- interface Entity<M extends Record<string, any> = any> {
27
- id: string | number;
28
- path: string;
29
- values: M;
30
- }
26
+ /** A flat database row with the id merged at the top level. */
31
27
 
32
28
  interface FindParams {
33
29
  limit?: number;
@@ -39,8 +35,8 @@ interface FindParams {
39
35
  searchString?: string;
40
36
  }
41
37
 
42
- interface FindResponse<M extends Record<string, any> = any> {
43
- data: Entity<M>[];
38
+ interface FindResult<M extends Record<string, any> = any> {
39
+ data: M[];
44
40
  meta: {
45
41
  total: number;
46
42
  limit: number;
@@ -57,55 +53,55 @@ interface QueryBuilder<M extends Record<string, any> = any> {
57
53
  limit(count: number): QueryBuilder<M>;
58
54
  offset(count: number): QueryBuilder<M>;
59
55
  search(searchString: string): QueryBuilder<M>;
60
- find(): Promise<FindResponse<M>>;
61
- findOne(): Promise<Entity<M> | undefined>;
56
+ find(): Promise<FindResult<M>>;
62
57
  count(): Promise<number>;
63
58
  }
64
59
 
65
60
  interface CollectionClient<M extends Record<string, any> = any> {
66
- find(params?: FindParams): Promise<FindResponse<M>>;
67
- findById(id: string | number): Promise<Entity<M> | undefined>;
68
- create(data: Partial<M>, id?: string | number): Promise<Entity<M>>;
69
- update(id: string | number, data: Partial<M>): Promise<Entity<M>>;
61
+ find(params?: FindParams): Promise<FindResult<M>>;
62
+ findById(id: string | number): Promise<M | undefined>;
63
+ create(data: Partial<M>, id?: string | number): Promise<M>;
64
+ update(id: string | number, data: Partial<M>): Promise<M>;
70
65
  delete(id: string | number): Promise<void>;
71
66
  where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;
72
67
  orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilder<M>;
73
68
  limit(count: number): QueryBuilder<M>;
74
69
  offset(count: number): QueryBuilder<M>;
75
70
  search(searchString: string): QueryBuilder<M>;
76
- listen?(params: FindParams | undefined, onUpdate: (response: FindResponse<M>) => void, onError?: (error: Error) => void): () => void;
77
- listenById?(id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void): () => void;
71
+ listen?(params: FindParams | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
72
+ listenById?(id: string | number, onUpdate: (row: M | undefined) => void, onError?: (error: Error) => void): () => void;
78
73
  count?(params?: FindParams): Promise<number>;
79
74
  }
80
75
 
81
- interface RebaseUser {
76
+ interface User {
82
77
  uid: string;
83
78
  email: string | null;
84
79
  displayName: string | null;
85
80
  photoURL: string | null;
86
- emailVerified?: boolean;
87
- roles?: string[];
88
81
  providerId: string;
89
82
  isAnonymous: boolean;
83
+ emailVerified?: boolean;
84
+ roles?: string[];
85
+ metadata?: Record<string, unknown>;
90
86
  }
91
87
 
92
88
  interface RebaseSession {
93
89
  accessToken: string;
94
90
  refreshToken: string;
95
91
  expiresAt: number;
96
- user: RebaseUser;
92
+ user: User;
97
93
  }
98
94
 
99
95
  type AuthChangeEvent = 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED';
100
96
 
101
97
  interface RebaseAuth {
102
- signInWithEmail(email: string, password: string): Promise<{ user: RebaseUser; accessToken: string; refreshToken: string }>;
103
- signUp(email: string, password: string, displayName?: string): Promise<{ user: RebaseUser; accessToken: string; refreshToken: string }>;
104
- signInWithGoogle(idToken: string): Promise<{ user: RebaseUser; accessToken: string; refreshToken: string }>;
98
+ signInWithEmail(email: string, password: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;
99
+ signUp(email: string, password: string, displayName?: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;
100
+ signInWithGoogle(idToken: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;
105
101
  signOut(): Promise<void>;
106
102
  refreshSession(): Promise<RebaseSession>;
107
- getUser(): Promise<RebaseUser>;
108
- updateUser(updates: { displayName?: string; photoURL?: string }): Promise<RebaseUser>;
103
+ getUser(): Promise<User>;
104
+ updateUser(updates: { displayName?: string; photoURL?: string }): Promise<User>;
109
105
  resetPasswordForEmail(email: string): Promise<{ success: boolean; message: string }>;
110
106
  resetPassword(token: string, password: string): Promise<{ success: boolean; message: string }>;
111
107
  changePassword(oldPassword: string, newPassword: string): Promise<{ success: boolean; message: string }>;
@@ -26,7 +26,7 @@ import {
26
26
  Typography
27
27
  } from "@rebasepro/ui";
28
28
  import { useRebaseContext, useSnackbarController, ErrorView, useTranslation } from "@rebasepro/core";
29
- import { isPostgresCollection } from "@rebasepro/types";
29
+ import { isPostgresCollectionConfig } from "@rebasepro/types";
30
30
  import { REBASE_INTERNAL_SCHEMAS, REBASE_INTERNAL_PREFIXES, JUNCTION_TABLES_SQL } from "@rebasepro/common";
31
31
  import { PolicyEditor } from "./PolicyEditor";
32
32
 
@@ -388,35 +388,22 @@ status: "live" };
388
388
  });
389
389
 
390
390
  // Merge code-based policies
391
- if (activeCollection && isPostgresCollection(activeCollection) && activeCollection.securityRules) {
392
- activeCollection.securityRules.forEach((rule: { name?: string, mode?: string, operation?: string, roles?: string[], using?: string, withCheck?: string }) => {
391
+ if (activeCollection && isPostgresCollectionConfig(activeCollection) && activeCollection.securityRules) {
392
+ activeCollection.securityRules.forEach((rule) => {
393
393
  const ruleName = rule.name;
394
394
  if (!ruleName) return;
395
395
 
396
- if (policiesMap[ruleName]) {
397
- // It exists in Postgres, but we have a code definition (potentially edited)
398
- policiesMap[ruleName] = {
399
- policyname: ruleName,
400
- tablename: activeTableData.tableName,
401
- permissive: (rule.mode || "permissive").toUpperCase() as PostgresPolicy["permissive"],
402
- cmd: (rule.operation || "ALL").toUpperCase() as PostgresPolicy["cmd"],
403
- roles: rule.roles || ["public"],
404
- qual: rule.using || null,
405
- with_check: rule.withCheck || null,
406
- status: "both"
407
- };
408
- } else {
409
- policiesMap[ruleName] = {
410
- policyname: ruleName,
411
- tablename: activeTableData.tableName,
412
- permissive: (rule.mode || "permissive").toUpperCase() as PostgresPolicy["permissive"],
413
- cmd: (rule.operation || "ALL").toUpperCase() as PostgresPolicy["cmd"],
414
- roles: rule.roles || ["public"],
415
- qual: rule.using || null,
416
- with_check: rule.withCheck || null,
417
- status: "code_only"
418
- };
419
- }
396
+ policiesMap[ruleName] = {
397
+ policyname: ruleName,
398
+ tablename: activeTableData.tableName,
399
+ permissive: (rule.mode || "permissive").toUpperCase() as PostgresPolicy["permissive"],
400
+ cmd: (rule.operation || "ALL").toUpperCase() as PostgresPolicy["cmd"],
401
+ roles: [...(rule.roles ?? ["public"])],
402
+ qual: rule.using || null,
403
+ with_check: rule.withCheck || null,
404
+ // "both" = defined in code and live in Postgres (potentially edited)
405
+ status: policiesMap[ruleName] ? "both" : "code_only"
406
+ };
420
407
  });
421
408
  }
422
409
 
@@ -734,7 +721,7 @@ message: e instanceof Error ? e.message : String(e) });
734
721
  roles: newPolicy.roles
735
722
  };
736
723
 
737
- const existingRules = (isPostgresCollection(activeCollection) ? activeCollection.securityRules : undefined) || [];
724
+ const existingRules = (isPostgresCollectionConfig(activeCollection) ? activeCollection.securityRules : undefined) || [];
738
725
  let newRules;
739
726
  if (editingPolicy === "new") {
740
727
  newRules = [...existingRules, rule];
@@ -829,7 +816,7 @@ message: e instanceof Error ? e.message : String(e) });
829
816
  </Typography>
830
817
  <Typography variant="caption" className="opacity-80">
831
818
  This is an auto-generated junction table for a many-to-many relation.
832
- Its access is typically managed through the related collections' policies.
819
+ Its access is typically managed through the related collections&apos; policies.
833
820
  You can still add RLS policies directly if needed.
834
821
  </Typography>
835
822
  </div>
@@ -845,7 +832,7 @@ message: e instanceof Error ? e.message : String(e) });
845
832
  Unmapped Table
846
833
  </Typography>
847
834
  <Typography variant="caption" className="opacity-80">
848
- This table exists in the database but isn't mapped to a collection definition.
835
+ This table exists in the database but isn&apos;t mapped to a collection definition.
849
836
  Import it into a Schema configuration file to manage security policies visually.
850
837
  </Typography>
851
838
  </div>
@@ -926,7 +913,7 @@ message: e instanceof Error ? e.message : String(e) });
926
913
  roles: policy.roles
927
914
  };
928
915
 
929
- const existingRules = (isPostgresCollection(activeCollection) ? activeCollection.securityRules : undefined) || [];
916
+ const existingRules = (isPostgresCollectionConfig(activeCollection) ? activeCollection.securityRules : undefined) || [];
930
917
  const newRules = [...existingRules, rule];
931
918
 
932
919
  try {
@@ -1,6 +1,6 @@
1
1
 
2
2
  import { IconForView } from "@rebasepro/core";
3
- import { useStudioCollectionRegistry, useStudioSideEntityController } from "@rebasepro/core";
3
+ import { useStudioCollectionRegistry, useStudioSidePanelController } from "@rebasepro/core";
4
4
  import React, { useState, useEffect, useCallback, useRef, useMemo } from "react";
5
5
  import { createPortal } from "react-dom";
6
6
  import {
@@ -213,7 +213,7 @@ const getStoragePrefix = (baseUrl?: string) => {
213
213
 
214
214
  export const SQLEditor = () => {
215
215
  const { databaseAdmin, client } = useRebaseContext();
216
- const sideEntityController = useStudioSideEntityController();
216
+ const sidePanelController = useStudioSidePanelController();
217
217
  const snackbarController = useSnackbarController();
218
218
  const collectionRegistry = useStudioCollectionRegistry();
219
219
 
@@ -1100,7 +1100,7 @@ id: String(ra.entityId) })}>
1100
1100
  className="text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors"
1101
1101
  onClick={(e) => {
1102
1102
  e.stopPropagation();
1103
- sideEntityController?.open({
1103
+ sidePanelController?.open({
1104
1104
  path: ra.collection.collection.slug,
1105
1105
  entityId: ra.entityId,
1106
1106
  collection: ra.collection.collection,
@@ -1133,7 +1133,7 @@ id: String(ra.entityId) })}>
1133
1133
  key={ra.collection.tableName}
1134
1134
  dense
1135
1135
  onClick={() => {
1136
- sideEntityController?.open({
1136
+ sidePanelController?.open({
1137
1137
  path: ra.collection.collection.slug,
1138
1138
  entityId: ra.entityId,
1139
1139
  collection: ra.collection.collection,
@@ -29,8 +29,8 @@ import {
29
29
  useStudioCollectionRegistry,
30
30
  IconForView
31
31
  } from "@rebasepro/core";
32
- import type { EntityCollection } from "@rebasepro/types";
33
- import { isPostgresCollection } from "@rebasepro/types";
32
+ import type { CollectionConfig } from "@rebasepro/types";
33
+ import { isPostgresCollectionConfig } from "@rebasepro/types";
34
34
  import { useSchemaGraph } from "./useSchemaGraph";
35
35
  import type { TableNodeData } from "./useSchemaGraph";
36
36
  import { TableNode } from "./TableNode";
@@ -52,7 +52,7 @@ const edgeTypes = {
52
52
  function SchemaVisualizerCanvas({
53
53
  collections
54
54
  }: {
55
- collections: EntityCollection[];
55
+ collections: CollectionConfig[];
56
56
  }) {
57
57
  const reactFlowInstance = useReactFlow();
58
58
  const {
@@ -152,7 +152,7 @@ duration: 400 }
152
152
 
153
153
  // Group collections for sidebar
154
154
  const postgresCollections = useMemo(
155
- () => collections.filter(isPostgresCollection),
155
+ () => collections.filter(isPostgresCollectionConfig),
156
156
  [collections]
157
157
  );
158
158
 
@@ -184,7 +184,7 @@ duration: 400 }
184
184
  junctions: junctionNodes.length,
185
185
  withRls: postgresCollections.filter(
186
186
  (c) =>
187
- isPostgresCollection(c) &&
187
+ isPostgresCollectionConfig(c) &&
188
188
  c.securityRules &&
189
189
  c.securityRules.length > 0
190
190
  ).length
@@ -293,7 +293,7 @@ duration: 400 }
293
293
  )}
294
294
  </div>
295
295
  <div className="flex items-center gap-1 shrink-0 ml-1">
296
- {isPostgresCollection(collection) &&
296
+ {isPostgresCollectionConfig(collection) &&
297
297
  collection.securityRules &&
298
298
  collection.securityRules
299
299
  .length > 0 && (
@@ -629,7 +629,7 @@ duration: 400 }
629
629
  export const SchemaVisualizer = () => {
630
630
  const { collections: registryCollections } =
631
631
  useStudioCollectionRegistry() as {
632
- collections?: EntityCollection[];
632
+ collections?: CollectionConfig[];
633
633
  };
634
634
 
635
635
  // Merge registry collections with any passed collections
@@ -1,8 +1,8 @@
1
1
  import { useMemo, useState, useCallback, useEffect } from "react";
2
2
  import type { Node, Edge } from "@xyflow/react";
3
3
  import { MarkerType } from "@xyflow/react";
4
- import { isPostgresCollection } from "@rebasepro/types";
5
- import type { EntityCollection, PostgresCollection, Relation } from "@rebasepro/types";
4
+ import { isPostgresCollectionConfig } from "@rebasepro/types";
5
+ import type { CollectionConfig, PostgresCollectionConfig, Relation } from "@rebasepro/types";
6
6
  import { resolveCollectionRelations } from "@rebasepro/common";
7
7
  import { getLayoutedElements, getCardinalityLabel, getTypeLabel, NODE_WIDTH } from "./schema-visualizer.utils";
8
8
  import type { LayoutDirection, RelationEdgeData } from "./schema-visualizer.utils";
@@ -36,9 +36,9 @@ export interface TableNodeData {
36
36
  [key: string]: unknown;
37
37
  }
38
38
 
39
- // ─── Extract columns from an EntityCollection ─────────────────────────
39
+ // ─── Extract columns from a CollectionConfig ─────────────────────────
40
40
 
41
- const extractColumns = (collection: EntityCollection): ColumnInfo[] => {
41
+ const extractColumns = (collection: CollectionConfig): ColumnInfo[] => {
42
42
  const columns: ColumnInfo[] = [];
43
43
  const properties = collection.properties ?? {};
44
44
 
@@ -79,7 +79,7 @@ const extractColumns = (collection: EntityCollection): ColumnInfo[] => {
79
79
  }
80
80
 
81
81
  // Add FK columns from owning relations
82
- if (isPostgresCollection(collection)) {
82
+ if (isPostgresCollectionConfig(collection)) {
83
83
  try {
84
84
  const resolvedRelations = resolveCollectionRelations(collection);
85
85
  for (const rel of Object.values(resolvedRelations)) {
@@ -117,7 +117,7 @@ const extractColumns = (collection: EntityCollection): ColumnInfo[] => {
117
117
  // ─── Build graph from collections ─────────────────────────────────────
118
118
 
119
119
  const buildGraph = (
120
- collections: EntityCollection[],
120
+ collections: CollectionConfig[],
121
121
  direction: LayoutDirection
122
122
  ): { nodes: Node[]; edges: Edge[] } => {
123
123
  const nodes: Node[] = [];
@@ -143,7 +143,7 @@ const buildGraph = (
143
143
 
144
144
  // 1. Create nodes for each collection
145
145
  for (const collection of collections) {
146
- if (!isPostgresCollection(collection)) continue;
146
+ if (!isPostgresCollectionConfig(collection)) continue;
147
147
 
148
148
  const tableName = collection.table ?? collection.slug;
149
149
  const nodeId = `table-${tableName}`;
@@ -177,7 +177,7 @@ y: 0 },
177
177
  const processedJunctions = new Set<string>();
178
178
 
179
179
  for (const collection of collections) {
180
- if (!isPostgresCollection(collection)) continue;
180
+ if (!isPostgresCollectionConfig(collection)) continue;
181
181
 
182
182
  const tableName = collection.table ?? collection.slug;
183
183
  const sourceNodeId = tableToNodeId.get(tableName);
@@ -191,14 +191,14 @@ y: 0 },
191
191
  }
192
192
 
193
193
  for (const [relationKey, rel] of Object.entries(resolvedRelations)) {
194
- let targetCollection: EntityCollection;
194
+ let targetCollection: CollectionConfig;
195
195
  try {
196
196
  targetCollection = rel.target();
197
197
  } catch {
198
198
  continue;
199
199
  }
200
200
 
201
- if (!isPostgresCollection(targetCollection)) continue;
201
+ if (!isPostgresCollectionConfig(targetCollection)) continue;
202
202
 
203
203
  const targetTable = targetCollection.table ?? targetCollection.slug;
204
204
  const targetNodeId = tableToNodeId.get(targetTable);
@@ -356,7 +356,7 @@ export interface UseSchemaGraphResult {
356
356
  }
357
357
 
358
358
  export const useSchemaGraph = (
359
- collections: EntityCollection[] | undefined
359
+ collections: CollectionConfig[] | undefined
360
360
  ): UseSchemaGraphResult => {
361
361
  const [direction, setDirection] = useState<LayoutDirection>("LR");
362
362
  const [version, setVersion] = useState(0);
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ export {
6
6
  StudioBridgeProvider,
7
7
  StudioBridgeContext,
8
8
  useStudioCollectionRegistry,
9
- useStudioSideEntityController,
9
+ useStudioSidePanelController,
10
10
  useStudioUrlController,
11
11
  useStudioNavigationState,
12
12
  useStudioBreadcrumbs
@@ -1,4 +1,4 @@
1
- import type { EntityCollection, PostgresProperties, Property, StringProperty, NumberProperty, ArrayProperty, TableColumnInfo, TableMetadata, SecurityOperation, SecurityRule } from "@rebasepro/types";
1
+ import type { CollectionConfig, PostgresProperties, 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.
@@ -194,13 +194,13 @@ label: v.replace(/_/g, " ").replace(/\b\w/g, (c: string) => c.toUpperCase()) }))
194
194
  }
195
195
 
196
196
  /**
197
- * Builds a partial EntityCollection from PostgreSQL table metadata.
197
+ * Builds a partial CollectionConfig from PostgreSQL table metadata.
198
198
  * This is used when creating a new collection from an existing database table.
199
199
  */
200
200
  export function buildCollectionFromTableMetadata(
201
201
  tableName: string,
202
202
  metadata: TableMetadata
203
- ): Partial<EntityCollection> {
203
+ ): Partial<CollectionConfig> {
204
204
  const properties: Record<string, Property> = {};
205
205
  const propertiesOrder: string[] = [];
206
206
  const relations: Array<{
@@ -2,7 +2,7 @@
2
2
  import { describe, it, expect } from "@jest/globals";
3
3
  import { determineTableAndPK, extractTablesFromQuery, resolveQueryCollections } from "./sql_utils";
4
4
  import { TableInfo } from "../components/SQLEditor/SQLEditor";
5
- import { EntityCollection } from "@rebasepro/types";
5
+ import { CollectionConfig } from "@rebasepro/types";
6
6
 
7
7
  const mockSchemas: Record<string, TableInfo[]> = {
8
8
  "public": [
@@ -81,31 +81,31 @@ isPrimaryKey: false }
81
81
  ]
82
82
  };
83
83
 
84
- const mockCollections: EntityCollection[] = [
84
+ const mockCollections: CollectionConfig[] = [
85
85
  {
86
86
  slug: "users",
87
87
  name: "Users",
88
88
  table: "users",
89
89
  properties: {}
90
- } as EntityCollection,
90
+ } as CollectionConfig,
91
91
  {
92
92
  slug: "roles",
93
93
  name: "Roles",
94
94
  table: "roles",
95
95
  properties: {}
96
- } as EntityCollection,
96
+ } as CollectionConfig,
97
97
  {
98
98
  slug: "blogPosts",
99
99
  name: "Blog Posts",
100
100
  table: "blog_posts",
101
101
  properties: {}
102
- } as EntityCollection,
102
+ } as CollectionConfig,
103
103
  {
104
104
  slug: "blog-entries", // slug with hyphen, no table → falls back to snake_case "blog_entries"
105
105
  name: "Blog Entries",
106
106
  table: "",
107
107
  properties: {}
108
- } as EntityCollection
108
+ } as CollectionConfig
109
109
  ];
110
110
 
111
111
  describe("determineTableAndPK", () => {
@@ -1,6 +1,6 @@
1
1
  import { parseFirst } from "pgsql-ast-parser";
2
2
  import { TableInfo } from "../components/SQLEditor/SQLEditor";
3
- import { EntityCollection } from "@rebasepro/types";
3
+ import { CollectionConfig } from "@rebasepro/types";
4
4
 
5
5
  /**
6
6
  * A table extracted from a SQL query's FROM/JOIN clauses.
@@ -19,7 +19,7 @@ export interface ResolvedQueryCollection {
19
19
  /** SQL alias if present (e.g. "bp") */
20
20
  tableAlias?: string;
21
21
  /** The matched collection */
22
- collection: EntityCollection;
22
+ collection: CollectionConfig;
23
23
  /** Columns from this table that are present in the result set */
24
24
  columns: string[];
25
25
  /** The result column name that holds the primary key for this table (e.g. "id", "author_id") */
@@ -77,7 +77,7 @@ import { toSnakeCase } from "@rebasepro/utils";
77
77
  export function resolveQueryCollections(
78
78
  sqlString: string,
79
79
  schemas: Record<string, TableInfo[]>,
80
- collections: EntityCollection[],
80
+ collections: CollectionConfig[],
81
81
  resultColumns?: string[]
82
82
  ): ResolvedQueryCollection[] {
83
83
  const tables = extractTablesFromQuery(sqlString);