@rebasepro/studio 0.7.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 (38) hide show
  1. package/README.md +1 -1
  2. package/dist/{JSEditor-DSucz6wV.js → JSEditor-Sgmw1SHC.js} +28 -32
  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/{StorageView-CvrnHmDG.js → StorageView-BMhD29YO.js} +33 -5
  11. package/dist/StorageView-BMhD29YO.js.map +1 -0
  12. package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +2 -2
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.es.js +110 -82
  15. package/dist/index.es.js.map +1 -1
  16. package/dist/index.umd.js +174 -133
  17. package/dist/index.umd.js.map +1 -1
  18. package/dist/utils/pgColumnToProperty.d.ts +3 -3
  19. package/dist/utils/sql_utils.d.ts +3 -3
  20. package/package.json +17 -17
  21. package/src/components/JSEditor/JSEditor.tsx +7 -7
  22. package/src/components/JSEditor/JSMonacoEditor.tsx +23 -27
  23. package/src/components/RLSEditor/RLSEditor.tsx +18 -31
  24. package/src/components/SQLEditor/SQLEditor.tsx +4 -4
  25. package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +7 -7
  26. package/src/components/SchemaVisualizer/useSchemaGraph.ts +11 -11
  27. package/src/components/StorageView/StorageView.tsx +43 -4
  28. package/src/components/StudioHomePage.tsx +139 -84
  29. package/src/index.ts +1 -1
  30. package/src/utils/pgColumnToProperty.ts +4 -4
  31. package/src/utils/sql_utils.test.ts +6 -6
  32. package/src/utils/sql_utils.ts +3 -3
  33. package/dist/JSEditor-DSucz6wV.js.map +0 -1
  34. package/dist/RLSEditor-BM64laoW.js.map +0 -1
  35. package/dist/SQLEditor-CuAhR-zr.js.map +0 -1
  36. package/dist/SchemaVisualizer-OibKoD3g.js.map +0 -1
  37. package/dist/StorageView-CvrnHmDG.js.map +0 -1
  38. package/src/vite-env.d.ts +0 -1
package/dist/index.umd.js CHANGED
@@ -60,8 +60,6 @@
60
60
  var SECTIONS = [
61
61
  {
62
62
  label: "Database",
63
- dotColor: "bg-emerald-400",
64
- iconColor: "text-emerald-400",
65
63
  tools: [
66
64
  {
67
65
  path: "/schema",
@@ -103,8 +101,6 @@
103
101
  },
104
102
  {
105
103
  label: "Compute",
106
- dotColor: "bg-blue-400",
107
- iconColor: "text-blue-400",
108
104
  tools: [{
109
105
  path: "/js",
110
106
  name: "JS Console",
@@ -119,8 +115,6 @@
119
115
  },
120
116
  {
121
117
  label: "API",
122
- dotColor: "bg-violet-400",
123
- iconColor: "text-violet-400",
124
118
  tools: [{
125
119
  path: "/api",
126
120
  name: "API Explorer",
@@ -130,8 +124,6 @@
130
124
  },
131
125
  {
132
126
  label: "Storage",
133
- dotColor: "bg-amber-400",
134
- iconColor: "text-amber-400",
135
127
  tools: [{
136
128
  path: "/storage",
137
129
  name: "Storage",
@@ -141,8 +133,6 @@
141
133
  },
142
134
  {
143
135
  label: "Access Control",
144
- dotColor: "bg-rose-400",
145
- iconColor: "text-rose-400",
146
136
  tools: [
147
137
  {
148
138
  path: "/users",
@@ -165,6 +155,33 @@
165
155
  ]
166
156
  }
167
157
  ];
158
+ var COLLAPSED_STORAGE_KEY = "rebase-studio-home-collapsed";
159
+ function useStudioCollapsedGroups(groupNames) {
160
+ const [collapsed, setCollapsed] = (0, react.useState)(() => {
161
+ try {
162
+ const stored = localStorage.getItem(COLLAPSED_STORAGE_KEY);
163
+ return stored ? new Set(JSON.parse(stored)) : /* @__PURE__ */ new Set();
164
+ } catch {
165
+ return /* @__PURE__ */ new Set();
166
+ }
167
+ });
168
+ const isGroupCollapsed = (name) => collapsed.has(name);
169
+ const toggleGroupCollapsed = (name) => {
170
+ setCollapsed((prev) => {
171
+ const next = new Set(prev);
172
+ if (next.has(name)) next.delete(name);
173
+ else next.add(name);
174
+ try {
175
+ localStorage.setItem(COLLAPSED_STORAGE_KEY, JSON.stringify([...next]));
176
+ } catch {}
177
+ return next;
178
+ });
179
+ };
180
+ return {
181
+ isGroupCollapsed,
182
+ toggleGroupCollapsed
183
+ };
184
+ }
168
185
  function StudioHomePage({ additionalActions, additionalChildrenStart, additionalChildrenEnd, sections, hiddenGroups }) {
169
186
  const context = (0, _rebasepro_core.useRebaseContext)();
170
187
  const breadcrumbs = (0, _rebasepro_core.useStudioBreadcrumbs)();
@@ -174,9 +191,11 @@
174
191
  }, [breadcrumbs.set]);
175
192
  const { containerRef } = (0, _rebasepro_core.useRestoreScroll)();
176
193
  const pluginActions = (0, _rebasepro_core.useSlot)("home.actions", { context });
194
+ const filteredSections = (0, react.useMemo)(() => SECTIONS.filter((s) => !hiddenGroups?.includes(s.label)), [hiddenGroups]);
195
+ const { isGroupCollapsed, toggleGroupCollapsed } = useStudioCollapsedGroups((0, react.useMemo)(() => filteredSections.map((s) => s.label), [filteredSections]));
177
196
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
178
197
  ref: containerRef,
179
- className: "py-2 overflow-auto h-full w-full",
198
+ className: "py-2 overflow-auto h-full w-full bg-surface-50 dark:bg-surface-800",
180
199
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Container, {
181
200
  maxWidth: "6xl",
182
201
  children: [
@@ -189,68 +208,77 @@
189
208
  children: [additionalActions, pluginActions]
190
209
  }),
191
210
  additionalChildrenStart,
192
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
193
- className: "flex flex-col gap-8 pt-2",
194
- children: SECTIONS.map((section) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
195
- "aria-label": section.label,
196
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
197
- variant: "caption",
198
- component: "h2",
199
- color: "secondary",
200
- className: "py-2 font-medium uppercase text-sm text-surface-600 dark:text-surface-400",
201
- children: section.label
202
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
203
- className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 mt-2",
204
- children: section.tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Card, {
205
- onClick: () => {
206
- navigate(tool.path);
207
- context.analyticsController?.onAnalyticsEvent?.("home_navigate_to_view", { path: tool.path });
208
- },
209
- className: (0, _rebasepro_ui.cls)("h-full px-4 py-2.5 cursor-pointer transition-all duration-200 ease-in-out", "hover:-translate-y-0.5 hover:shadow-md hover:shadow-primary/5"),
210
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
211
- className: "flex flex-col items-start h-full",
212
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
213
- className: "grow w-full",
214
- children: [
215
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
216
- className: (0, _rebasepro_ui.cls)("h-6 flex items-center", section.iconColor),
217
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_core.IconForView, {
218
- collectionOrView: {
219
- slug: tool.path,
220
- name: tool.name,
221
- icon: tool.icon
222
- },
223
- size: "small"
224
- })
225
- }),
226
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
227
- gutterBottom: true,
228
- variant: "subtitle1",
229
- className: "mt-1 font-semibold",
230
- component: "h2",
231
- children: tool.name
232
- }),
233
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
234
- variant: "caption",
235
- color: "secondary",
236
- component: "div",
237
- children: tool.description
238
- })
239
- ]
240
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
241
- style: { alignSelf: "flex-end" },
242
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
243
- className: "p-2",
244
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.ArrowRightIcon, {
245
- className: "text-primary",
246
- size: _rebasepro_ui.iconSize.small
247
- })
211
+ filteredSections.map((section) => {
212
+ const sectionCollapsed = isGroupCollapsed(section.label);
213
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
214
+ className: "my-10",
215
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.ExpandablePanel, {
216
+ invisible: true,
217
+ expanded: !sectionCollapsed,
218
+ onExpandedChange: (open) => {
219
+ if (open !== !sectionCollapsed) toggleGroupCollapsed(section.label);
220
+ },
221
+ className: "mt-6",
222
+ titleClassName: (0, _rebasepro_ui.cls)("min-h-0 p-0 border-none", "rounded flex items-center justify-between w-full", "hover:bg-transparent", "cursor-pointer select-none", sectionCollapsed && "bg-surface-100 dark:bg-surface-900/50"),
223
+ innerClassName: "mt-4 pt-0",
224
+ title: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
225
+ variant: "caption",
226
+ component: "h2",
227
+ color: "secondary",
228
+ className: (0, _rebasepro_ui.cls)("px-4 py-1 rounded", "font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70"),
229
+ children: section.label
230
+ }),
231
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
232
+ className: "mt-4 pt-0",
233
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
234
+ className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4",
235
+ children: section.tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Card, {
236
+ onClick: () => {
237
+ navigate(tool.path);
238
+ context.analyticsController?.onAnalyticsEvent?.("home_navigate_to_view", { path: tool.path });
239
+ },
240
+ className: (0, _rebasepro_ui.cls)("group h-full p-4 cursor-pointer transition-colors duration-150 ease-in-out", "hover:bg-primary/5 dark:hover:bg-primary/5"),
241
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
242
+ className: "flex flex-col h-full",
243
+ children: [
244
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
245
+ className: "flex items-center w-full justify-between mb-1",
246
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
247
+ className: "flex items-center gap-3",
248
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
249
+ className: "flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500 transition-colors duration-150 group-hover:text-primary dark:group-hover:text-primary",
250
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_core.IconForView, {
251
+ collectionOrView: {
252
+ slug: tool.path,
253
+ name: tool.name,
254
+ icon: tool.icon
255
+ },
256
+ size: "small"
257
+ })
258
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
259
+ variant: "subtitle1",
260
+ component: "h2",
261
+ children: tool.name
262
+ })]
263
+ })
264
+ }),
265
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
266
+ className: "pl-8",
267
+ children: tool.description && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
268
+ variant: "caption",
269
+ color: "secondary",
270
+ component: "div",
271
+ children: tool.description
272
+ })
273
+ }),
274
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "grow" })
275
+ ]
248
276
  })
249
- })]
277
+ }, tool.path))
250
278
  })
251
- }, tool.path))
252
- })]
253
- }, section.label))
279
+ })
280
+ })
281
+ }, section.label);
254
282
  }),
255
283
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
256
284
  className: "mt-10 mb-6",
@@ -261,7 +289,7 @@
261
289
  variant: "caption",
262
290
  component: "h2",
263
291
  color: "secondary",
264
- className: "py-2 font-medium uppercase text-sm text-surface-600 dark:text-surface-400",
292
+ className: (0, _rebasepro_ui.cls)("px-4 py-1 rounded", "font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70"),
265
293
  children: "Quick Start"
266
294
  })
267
295
  }),
@@ -314,7 +342,7 @@
314
342
  variant: "caption",
315
343
  component: "h2",
316
344
  color: "secondary",
317
- className: "p-4 py-2 rounded font-medium uppercase text-sm text-surface-600 dark:text-surface-400",
345
+ className: (0, _rebasepro_ui.cls)("px-4 py-1 rounded", "font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70"),
318
346
  children: s.title
319
347
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
320
348
  className: "mt-4",
@@ -1638,7 +1666,7 @@
1638
1666
  };
1639
1667
  SQLEditor$1 = () => {
1640
1668
  const { databaseAdmin, client } = (0, _rebasepro_core.useRebaseContext)();
1641
- const sideEntityController = (0, _rebasepro_core.useStudioSideEntityController)();
1669
+ const sidePanelController = (0, _rebasepro_core.useStudioSidePanelController)();
1642
1670
  const snackbarController = (0, _rebasepro_core.useSnackbarController)();
1643
1671
  const collectionRegistry = (0, _rebasepro_core.useStudioCollectionRegistry)();
1644
1672
  const { t } = (0, _rebasepro_core.useTranslation)();
@@ -2400,7 +2428,7 @@
2400
2428
  className: "text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300 transition-colors",
2401
2429
  onClick: (e) => {
2402
2430
  e.stopPropagation();
2403
- sideEntityController?.open({
2431
+ sidePanelController?.open({
2404
2432
  path: ra.collection.collection.slug,
2405
2433
  entityId: ra.entityId,
2406
2434
  collection: ra.collection.collection,
@@ -2424,7 +2452,7 @@
2424
2452
  children: rowActions.map((ra) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.MenuItem, {
2425
2453
  dense: true,
2426
2454
  onClick: () => {
2427
- sideEntityController?.open({
2455
+ sidePanelController?.open({
2428
2456
  path: ra.collection.collection.slug,
2429
2457
  entityId: ra.entityId,
2430
2458
  collection: ra.collection.collection,
@@ -2839,11 +2867,7 @@
2839
2867
  REBASE_CLIENT_TYPES = `
2840
2868
  // ─── Rebase Client SDK Type Definitions ─────────────────────────────
2841
2869
 
2842
- interface Entity<M extends Record<string, any> = any> {
2843
- id: string | number;
2844
- path: string;
2845
- values: M;
2846
- }
2870
+ /** A flat database row with the id merged at the top level. */
2847
2871
 
2848
2872
  interface FindParams {
2849
2873
  limit?: number;
@@ -2855,8 +2879,8 @@ interface FindParams {
2855
2879
  searchString?: string;
2856
2880
  }
2857
2881
 
2858
- interface FindResponse<M extends Record<string, any> = any> {
2859
- data: Entity<M>[];
2882
+ interface FindResult<M extends Record<string, any> = any> {
2883
+ data: M[];
2860
2884
  meta: {
2861
2885
  total: number;
2862
2886
  limit: number;
@@ -2865,63 +2889,63 @@ interface FindResponse<M extends Record<string, any> = any> {
2865
2889
  };
2866
2890
  }
2867
2891
 
2868
- type FilterOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "in" | "not-in" | "array-contains" | "array-contains-any" | "is" | "is_not" | "like" | "ilike";
2892
+ type WhereFilterOp = "<" | "<=" | "==" | "!=" | ">=" | ">" | "in" | "not-in" | "array-contains" | "array-contains-any";
2869
2893
 
2870
2894
  interface QueryBuilder<M extends Record<string, any> = any> {
2871
- where(column: keyof M & string, operator: FilterOperator, value: any): QueryBuilder<M>;
2895
+ where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;
2872
2896
  orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilder<M>;
2873
2897
  limit(count: number): QueryBuilder<M>;
2874
2898
  offset(count: number): QueryBuilder<M>;
2875
2899
  search(searchString: string): QueryBuilder<M>;
2876
- find(): Promise<FindResponse<M>>;
2877
- findOne(): Promise<Entity<M> | undefined>;
2900
+ find(): Promise<FindResult<M>>;
2878
2901
  count(): Promise<number>;
2879
2902
  }
2880
2903
 
2881
2904
  interface CollectionClient<M extends Record<string, any> = any> {
2882
- find(params?: FindParams): Promise<FindResponse<M>>;
2883
- findById(id: string | number): Promise<Entity<M> | undefined>;
2884
- create(data: Partial<M>, id?: string | number): Promise<Entity<M>>;
2885
- update(id: string | number, data: Partial<M>): Promise<Entity<M>>;
2905
+ find(params?: FindParams): Promise<FindResult<M>>;
2906
+ findById(id: string | number): Promise<M | undefined>;
2907
+ create(data: Partial<M>, id?: string | number): Promise<M>;
2908
+ update(id: string | number, data: Partial<M>): Promise<M>;
2886
2909
  delete(id: string | number): Promise<void>;
2887
- where(column: keyof M & string, operator: FilterOperator, value: any): QueryBuilder<M>;
2910
+ where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;
2888
2911
  orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilder<M>;
2889
2912
  limit(count: number): QueryBuilder<M>;
2890
2913
  offset(count: number): QueryBuilder<M>;
2891
2914
  search(searchString: string): QueryBuilder<M>;
2892
- listen?(params: FindParams | undefined, onUpdate: (response: FindResponse<M>) => void, onError?: (error: Error) => void): () => void;
2893
- listenById?(id: string | number, onUpdate: (entity: Entity<M> | undefined) => void, onError?: (error: Error) => void): () => void;
2915
+ listen?(params: FindParams | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
2916
+ listenById?(id: string | number, onUpdate: (row: M | undefined) => void, onError?: (error: Error) => void): () => void;
2894
2917
  count?(params?: FindParams): Promise<number>;
2895
2918
  }
2896
2919
 
2897
- interface RebaseUser {
2920
+ interface User {
2898
2921
  uid: string;
2899
2922
  email: string | null;
2900
2923
  displayName: string | null;
2901
2924
  photoURL: string | null;
2902
- emailVerified?: boolean;
2903
- roles?: string[];
2904
2925
  providerId: string;
2905
2926
  isAnonymous: boolean;
2927
+ emailVerified?: boolean;
2928
+ roles?: string[];
2929
+ metadata?: Record<string, unknown>;
2906
2930
  }
2907
2931
 
2908
2932
  interface RebaseSession {
2909
2933
  accessToken: string;
2910
2934
  refreshToken: string;
2911
2935
  expiresAt: number;
2912
- user: RebaseUser;
2936
+ user: User;
2913
2937
  }
2914
2938
 
2915
2939
  type AuthChangeEvent = 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED';
2916
2940
 
2917
2941
  interface RebaseAuth {
2918
- signInWithEmail(email: string, password: string): Promise<{ user: RebaseUser; accessToken: string; refreshToken: string }>;
2919
- signUp(email: string, password: string, displayName?: string): Promise<{ user: RebaseUser; accessToken: string; refreshToken: string }>;
2920
- signInWithGoogle(idToken: string): Promise<{ user: RebaseUser; accessToken: string; refreshToken: string }>;
2942
+ signInWithEmail(email: string, password: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;
2943
+ signUp(email: string, password: string, displayName?: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;
2944
+ signInWithGoogle(idToken: string): Promise<{ user: User; accessToken: string; refreshToken: string }>;
2921
2945
  signOut(): Promise<void>;
2922
2946
  refreshSession(): Promise<RebaseSession>;
2923
- getUser(): Promise<RebaseUser>;
2924
- updateUser(updates: { displayName?: string; photoURL?: string }): Promise<RebaseUser>;
2947
+ getUser(): Promise<User>;
2948
+ updateUser(updates: { displayName?: string; photoURL?: string }): Promise<User>;
2925
2949
  resetPasswordForEmail(email: string): Promise<{ success: boolean; message: string }>;
2926
2950
  resetPassword(token: string, password: string): Promise<{ success: boolean; message: string }>;
2927
2951
  changePassword(oldPassword: string, newPassword: string): Promise<{ success: boolean; message: string }>;
@@ -3611,7 +3635,7 @@ declare const context: JSEditorContext;
3611
3635
  const apiConfig = (0, _rebasepro_core.useApiConfig)();
3612
3636
  const snackbar = (0, _rebasepro_core.useSnackbarController)();
3613
3637
  const collectionRegistry = (0, _rebasepro_core.useStudioCollectionRegistry)();
3614
- const sideEntityController = (0, _rebasepro_core.useStudioSideEntityController)();
3638
+ const sidePanelController = (0, _rebasepro_core.useStudioSidePanelController)();
3615
3639
  const { t } = (0, _rebasepro_core.useTranslation)();
3616
3640
  const currentUser = rebaseContext.authController?.user;
3617
3641
  const [tabs, setTabs] = (0, react.useState)(() => loadFromStorage("tabs", [{
@@ -4246,7 +4270,7 @@ declare const context: JSEditorContext;
4246
4270
  className: "text-surface-400 dark:text-surface-500 hover:text-surface-600 dark:hover:text-surface-300",
4247
4271
  onClick: (e) => {
4248
4272
  e.stopPropagation();
4249
- sideEntityController.open({
4273
+ sidePanelController.open({
4250
4274
  path: ra.collection.collectionSlug,
4251
4275
  entityId: ra.entityId,
4252
4276
  collection: ra.collection.collection,
@@ -4270,7 +4294,7 @@ declare const context: JSEditorContext;
4270
4294
  children: rowActions.map((ra) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.MenuItem, {
4271
4295
  dense: true,
4272
4296
  onClick: () => {
4273
- sideEntityController.open({
4297
+ sidePanelController.open({
4274
4298
  path: ra.collection.collectionSlug,
4275
4299
  entityId: ra.entityId,
4276
4300
  collection: ra.collection.collection,
@@ -5296,28 +5320,18 @@ return result;
5296
5320
  status: "live"
5297
5321
  };
5298
5322
  });
5299
- if (activeCollection && (0, _rebasepro_types.isPostgresCollection)(activeCollection) && activeCollection.securityRules) activeCollection.securityRules.forEach((rule) => {
5323
+ if (activeCollection && (0, _rebasepro_types.isPostgresCollectionConfig)(activeCollection) && activeCollection.securityRules) activeCollection.securityRules.forEach((rule) => {
5300
5324
  const ruleName = rule.name;
5301
5325
  if (!ruleName) return;
5302
- if (policiesMap[ruleName]) policiesMap[ruleName] = {
5303
- policyname: ruleName,
5304
- tablename: activeTableData.tableName,
5305
- permissive: (rule.mode || "permissive").toUpperCase(),
5306
- cmd: (rule.operation || "ALL").toUpperCase(),
5307
- roles: rule.roles || ["public"],
5308
- qual: rule.using || null,
5309
- with_check: rule.withCheck || null,
5310
- status: "both"
5311
- };
5312
- else policiesMap[ruleName] = {
5326
+ policiesMap[ruleName] = {
5313
5327
  policyname: ruleName,
5314
5328
  tablename: activeTableData.tableName,
5315
5329
  permissive: (rule.mode || "permissive").toUpperCase(),
5316
5330
  cmd: (rule.operation || "ALL").toUpperCase(),
5317
- roles: rule.roles || ["public"],
5331
+ roles: [...rule.roles ?? ["public"]],
5318
5332
  qual: rule.using || null,
5319
5333
  with_check: rule.withCheck || null,
5320
- status: "code_only"
5334
+ status: policiesMap[ruleName] ? "both" : "code_only"
5321
5335
  };
5322
5336
  });
5323
5337
  return Object.values(policiesMap).sort((a, b) => a.policyname.localeCompare(b.policyname));
@@ -5698,7 +5712,7 @@ return result;
5698
5712
  withCheck: newPolicy.with_check || void 0,
5699
5713
  roles: newPolicy.roles
5700
5714
  };
5701
- const existingRules = ((0, _rebasepro_types.isPostgresCollection)(activeCollection) ? activeCollection.securityRules : void 0) || [];
5715
+ const existingRules = ((0, _rebasepro_types.isPostgresCollectionConfig)(activeCollection) ? activeCollection.securityRules : void 0) || [];
5702
5716
  let newRules;
5703
5717
  if (editingPolicy === "new") newRules = [...existingRules, rule];
5704
5718
  else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
@@ -5895,7 +5909,7 @@ return result;
5895
5909
  withCheck: policy.with_check || void 0,
5896
5910
  roles: policy.roles
5897
5911
  };
5898
- const newRules = [...((0, _rebasepro_types.isPostgresCollection)(activeCollection) ? activeCollection.securityRules : void 0) || [], rule];
5912
+ const newRules = [...((0, _rebasepro_types.isPostgresCollectionConfig)(activeCollection) ? activeCollection.securityRules : void 0) || [], rule];
5899
5913
  try {
5900
5914
  if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
5901
5915
  method: "POST",
@@ -6418,8 +6432,16 @@ return result;
6418
6432
  var StorageView$1;
6419
6433
  var init_StorageView = __esmMin((() => {
6420
6434
  StorageView$1 = () => {
6421
- const storageSource = (0, _rebasepro_core.useStorageSource)();
6435
+ const defaultStorageSource = (0, _rebasepro_core.useStorageSource)();
6436
+ const storageSources = (0, _rebasepro_core.useStorageSources)();
6422
6437
  const snackbarController = (0, _rebasepro_core.useSnackbarController)();
6438
+ const sourceKeys = (0, react.useMemo)(() => {
6439
+ const keys = Object.keys(storageSources.sources);
6440
+ if (!keys.includes(_rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY)) keys.unshift(_rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY);
6441
+ return keys;
6442
+ }, [storageSources.sources]);
6443
+ const [selectedSourceKey, setSelectedSourceKey] = (0, react.useState)(_rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY);
6444
+ const storageSource = storageSources.sources[selectedSourceKey] ?? defaultStorageSource;
6423
6445
  const [searchParams, setSearchParams] = (0, react_router_dom.useSearchParams)();
6424
6446
  const currentPath = searchParams.get("path") || "";
6425
6447
  const [loading, setLoading] = (0, react.useState)(true);
@@ -6483,7 +6505,11 @@ return result;
6483
6505
  }, []);
6484
6506
  (0, react.useEffect)(() => {
6485
6507
  fetchContents(currentPath);
6486
- }, [currentPath, fetchContents]);
6508
+ }, [
6509
+ currentPath,
6510
+ fetchContents,
6511
+ selectedSourceKey
6512
+ ]);
6487
6513
  const handleNavigate = (0, react.useCallback)((path) => {
6488
6514
  if (!path) setSearchParams({});
6489
6515
  else setSearchParams({ path });
@@ -7163,6 +7189,21 @@ return result;
7163
7189
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7164
7190
  className: "flex shrink-0 items-center justify-end gap-1.5 pr-1",
7165
7191
  children: [
7192
+ sourceKeys.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Select, {
7193
+ size: "small",
7194
+ position: "item-aligned",
7195
+ value: selectedSourceKey,
7196
+ onValueChange: (value) => {
7197
+ if (value) setSelectedSourceKey(value);
7198
+ },
7199
+ renderValue: (key) => {
7200
+ return storageSources.registry[key]?.label ?? (key === _rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY ? "Default" : key);
7201
+ },
7202
+ children: sourceKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
7203
+ value: key,
7204
+ children: storageSources.registry[key]?.label ?? (key === _rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY ? "Default" : key)
7205
+ }, key))
7206
+ }),
7166
7207
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
7167
7208
  title: "Grid view",
7168
7209
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
@@ -8000,7 +8041,7 @@ return result;
8000
8041
  enumValues
8001
8042
  });
8002
8043
  }
8003
- if ((0, _rebasepro_types.isPostgresCollection)(collection)) try {
8044
+ if ((0, _rebasepro_types.isPostgresCollectionConfig)(collection)) try {
8004
8045
  const resolvedRelations = (0, _rebasepro_common.resolveCollectionRelations)(collection);
8005
8046
  for (const rel of Object.values(resolvedRelations)) if (rel.direction === "owning" && rel.cardinality === "one" && rel.localKey) if (!columns.some((c) => c.name === rel.localKey)) columns.push({
8006
8047
  name: rel.localKey,
@@ -8037,7 +8078,7 @@ return result;
8037
8078
  return fk ? `source-${fk.name}` : "source-default";
8038
8079
  };
8039
8080
  for (const collection of collections) {
8040
- if (!(0, _rebasepro_types.isPostgresCollection)(collection)) continue;
8081
+ if (!(0, _rebasepro_types.isPostgresCollectionConfig)(collection)) continue;
8041
8082
  const tableName = collection.table ?? collection.slug;
8042
8083
  const nodeId = `table-${tableName}`;
8043
8084
  tableToNodeId.set(tableName, nodeId);
@@ -8066,7 +8107,7 @@ return result;
8066
8107
  }
8067
8108
  const processedJunctions = /* @__PURE__ */ new Set();
8068
8109
  for (const collection of collections) {
8069
- if (!(0, _rebasepro_types.isPostgresCollection)(collection)) continue;
8110
+ if (!(0, _rebasepro_types.isPostgresCollectionConfig)(collection)) continue;
8070
8111
  const tableName = collection.table ?? collection.slug;
8071
8112
  const sourceNodeId = tableToNodeId.get(tableName);
8072
8113
  if (!sourceNodeId) continue;
@@ -8083,7 +8124,7 @@ return result;
8083
8124
  } catch {
8084
8125
  continue;
8085
8126
  }
8086
- if (!(0, _rebasepro_types.isPostgresCollection)(targetCollection)) continue;
8127
+ if (!(0, _rebasepro_types.isPostgresCollectionConfig)(targetCollection)) continue;
8087
8128
  const targetTable = targetCollection.table ?? targetCollection.slug;
8088
8129
  const targetNodeId = tableToNodeId.get(targetTable);
8089
8130
  if (!targetNodeId) continue;
@@ -8556,7 +8597,7 @@ return result;
8556
8597
  localStorage.setItem("rebase_schema_viz_sidebar_size", sidebarSize.toString());
8557
8598
  } catch {}
8558
8599
  }, [sidebarSize]);
8559
- const postgresCollections = (0, react.useMemo)(() => collections.filter(_rebasepro_types.isPostgresCollection), [collections]);
8600
+ const postgresCollections = (0, react.useMemo)(() => collections.filter(_rebasepro_types.isPostgresCollectionConfig), [collections]);
8560
8601
  const filteredCollections = (0, react.useMemo)(() => {
8561
8602
  if (!searchQuery.trim()) return postgresCollections;
8562
8603
  const q = searchQuery.toLowerCase();
@@ -8567,7 +8608,7 @@ return result;
8567
8608
  tables: tableCount,
8568
8609
  relations: relationCount,
8569
8610
  junctions: junctionNodes.length,
8570
- withRls: postgresCollections.filter((c) => (0, _rebasepro_types.isPostgresCollection)(c) && c.securityRules && c.securityRules.length > 0).length
8611
+ withRls: postgresCollections.filter((c) => (0, _rebasepro_types.isPostgresCollectionConfig)(c) && c.securityRules && c.securityRules.length > 0).length
8571
8612
  }), [
8572
8613
  tableCount,
8573
8614
  relationCount,
@@ -8639,7 +8680,7 @@ return result;
8639
8680
  }),
8640
8681
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8641
8682
  className: "flex items-center gap-1 shrink-0 ml-1",
8642
- children: [(0, _rebasepro_types.isPostgresCollection)(collection) && collection.securityRules && collection.securityRules.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
8683
+ children: [(0, _rebasepro_types.isPostgresCollectionConfig)(collection) && collection.securityRules && collection.securityRules.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
8643
8684
  title: "RLS enabled",
8644
8685
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
8645
8686
  }), collection.history && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
@@ -11419,10 +11460,10 @@ return result;
11419
11460
  return _rebasepro_core.useStudioNavigationState;
11420
11461
  }
11421
11462
  });
11422
- Object.defineProperty(exports, "useStudioSideEntityController", {
11463
+ Object.defineProperty(exports, "useStudioSidePanelController", {
11423
11464
  enumerable: true,
11424
11465
  get: function() {
11425
- return _rebasepro_core.useStudioSideEntityController;
11466
+ return _rebasepro_core.useStudioSidePanelController;
11426
11467
  }
11427
11468
  });
11428
11469
  Object.defineProperty(exports, "useStudioUrlController", {