@rebasepro/studio 0.6.1 → 0.8.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 (37) hide show
  1. package/dist/ApiKeysView-D-_FSlNL.js +684 -0
  2. package/dist/ApiKeysView-D-_FSlNL.js.map +1 -0
  3. package/dist/{BranchesView-DncIRcZt.js → BranchesView-Dlg78EQ8.js} +4 -5
  4. package/dist/{BranchesView-DncIRcZt.js.map → BranchesView-Dlg78EQ8.js.map} +1 -1
  5. package/dist/{JSEditor-BhAbEjCP.js → JSEditor-Ca4XYGRp.js} +7 -7
  6. package/dist/JSEditor-Ca4XYGRp.js.map +1 -0
  7. package/dist/{LogsExplorer-CqtKILj8.js → LogsExplorer-J4xfsuv3.js} +49 -83
  8. package/dist/LogsExplorer-J4xfsuv3.js.map +1 -0
  9. package/dist/{RLSEditor-DpF1u9EC.js → RLSEditor-BM64laoW.js} +294 -107
  10. package/dist/RLSEditor-BM64laoW.js.map +1 -0
  11. package/dist/{SQLEditor-BLuq_zDM.js → SQLEditor-CuAhR-zr.js} +4 -4
  12. package/dist/{SQLEditor-BLuq_zDM.js.map → SQLEditor-CuAhR-zr.js.map} +1 -1
  13. package/dist/{SchemaVisualizer-BJK2u3C0.js → SchemaVisualizer-OibKoD3g.js} +2 -2
  14. package/dist/{SchemaVisualizer-BJK2u3C0.js.map → SchemaVisualizer-OibKoD3g.js.map} +1 -1
  15. package/dist/{StorageView-CvrnHmDG.js → StorageView-BMhD29YO.js} +33 -5
  16. package/dist/StorageView-BMhD29YO.js.map +1 -0
  17. package/dist/components/ApiKeys/ApiKeysView.d.ts +2 -0
  18. package/dist/index.es.js +141 -94
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +1221 -317
  21. package/dist/index.umd.js.map +1 -1
  22. package/package.json +8 -8
  23. package/src/components/ApiKeys/ApiKeysView.tsx +580 -0
  24. package/src/components/Branches/BranchesView.tsx +3 -4
  25. package/src/components/JSEditor/JSEditorSidebar.tsx +3 -3
  26. package/src/components/JSEditor/JSMonacoEditor.tsx +3 -3
  27. package/src/components/LogsExplorer/LogsExplorer.tsx +46 -84
  28. package/src/components/RLSEditor/RLSEditor.tsx +354 -117
  29. package/src/components/RebaseStudio.tsx +10 -1
  30. package/src/components/SQLEditor/SchemaBrowser.tsx +3 -3
  31. package/src/components/StorageView/StorageView.tsx +43 -4
  32. package/src/components/StudioHomePage.tsx +144 -85
  33. package/src/utils/pgColumnToProperty.ts +2 -2
  34. package/dist/JSEditor-BhAbEjCP.js.map +0 -1
  35. package/dist/LogsExplorer-CqtKILj8.js.map +0 -1
  36. package/dist/RLSEditor-DpF1u9EC.js.map +0 -1
  37. package/dist/StorageView-CvrnHmDG.js.map +0 -1
package/dist/index.umd.js CHANGED
@@ -1,5 +1,5 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@rebasepro/core"), require("react"), require("@rebasepro/ui"), require("react/jsx-runtime"), require("react-router-dom"), require("react-dom"), require("@monaco-editor/react"), require("pgsql-ast-parser"), require("@rebasepro/utils"), require("prism-react-renderer"), require("@rebasepro/client"), require("@rebasepro/types"), require("react-dropzone"), require("@xyflow/react"), require("@xyflow/react/dist/style.css"), require("@rebasepro/common"), require("dagre")) : typeof define === "function" && define.amd ? define([
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@rebasepro/core"), require("react"), require("@rebasepro/ui"), require("react/jsx-runtime"), require("react-router-dom"), require("react-dom"), require("@monaco-editor/react"), require("pgsql-ast-parser"), require("@rebasepro/utils"), require("prism-react-renderer"), require("@rebasepro/client"), require("@rebasepro/types"), require("@rebasepro/common"), require("react-dropzone"), require("@xyflow/react"), require("@xyflow/react/dist/style.css"), require("dagre")) : typeof define === "function" && define.amd ? define([
3
3
  "exports",
4
4
  "@rebasepro/core",
5
5
  "react",
@@ -13,13 +13,13 @@
13
13
  "prism-react-renderer",
14
14
  "@rebasepro/client",
15
15
  "@rebasepro/types",
16
+ "@rebasepro/common",
16
17
  "react-dropzone",
17
18
  "@xyflow/react",
18
19
  "@xyflow/react/dist/style.css",
19
- "@rebasepro/common",
20
20
  "dagre"
21
- ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["Rebase collection editor"] = {}, global._rebasepro_core, global.react, global._rebasepro_ui, global.react_jsx_runtime, global.react_router_dom, global.react_dom, global._monaco_editor_react, global.pgsql_ast_parser, global._rebasepro_utils, global.prism_react_renderer, global._rebasepro_client, global._rebasepro_types, global.react_dropzone, global._xyflow_react, global._xyflow_react_dist_style_css, global._rebasepro_common, global.dagre));
22
- })(this, function(exports, _rebasepro_core, react, _rebasepro_ui, react_jsx_runtime, react_router_dom, react_dom, _monaco_editor_react, pgsql_ast_parser, _rebasepro_utils, prism_react_renderer, _rebasepro_client, _rebasepro_types, react_dropzone, _xyflow_react, _xyflow_react_dist_style_css, _rebasepro_common, dagre) {
21
+ ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global["Rebase collection editor"] = {}, global._rebasepro_core, global.react, global._rebasepro_ui, global.react_jsx_runtime, global.react_router_dom, global.react_dom, global._monaco_editor_react, global.pgsql_ast_parser, global._rebasepro_utils, global.prism_react_renderer, global._rebasepro_client, global._rebasepro_types, global._rebasepro_common, global.react_dropzone, global._xyflow_react, global._xyflow_react_dist_style_css, global.dagre));
22
+ })(this, function(exports, _rebasepro_core, react, _rebasepro_ui, react_jsx_runtime, react_router_dom, react_dom, _monaco_editor_react, pgsql_ast_parser, _rebasepro_utils, prism_react_renderer, _rebasepro_client, _rebasepro_types, _rebasepro_common, react_dropzone, _xyflow_react, _xyflow_react_dist_style_css, dagre) {
23
23
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
24
24
  //#region \0rolldown/runtime.js
25
25
  var __create = Object.create;
@@ -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,21 +133,55 @@
141
133
  },
142
134
  {
143
135
  label: "Access Control",
144
- dotColor: "bg-rose-400",
145
- iconColor: "text-rose-400",
146
- tools: [{
147
- path: "/users",
148
- name: "Users",
149
- description: "Manage developers and assign roles in your workspace",
150
- icon: "group"
151
- }, {
152
- path: "/roles",
153
- name: "Roles",
154
- description: "Create and configure fine-grained access permissions",
155
- icon: "ShieldEllipsis"
156
- }]
136
+ tools: [
137
+ {
138
+ path: "/users",
139
+ name: "Users",
140
+ description: "Manage developers and assign roles in your workspace",
141
+ icon: "group"
142
+ },
143
+ {
144
+ path: "/roles",
145
+ name: "Roles",
146
+ description: "Create and configure fine-grained access permissions",
147
+ icon: "ShieldEllipsis"
148
+ },
149
+ {
150
+ path: "/api-keys",
151
+ name: "API Keys",
152
+ description: "Create and manage scoped keys for machine-to-machine access",
153
+ icon: "KeyRound"
154
+ }
155
+ ]
157
156
  }
158
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
+ }
159
185
  function StudioHomePage({ additionalActions, additionalChildrenStart, additionalChildrenEnd, sections, hiddenGroups }) {
160
186
  const context = (0, _rebasepro_core.useRebaseContext)();
161
187
  const breadcrumbs = (0, _rebasepro_core.useStudioBreadcrumbs)();
@@ -165,9 +191,11 @@
165
191
  }, [breadcrumbs.set]);
166
192
  const { containerRef } = (0, _rebasepro_core.useRestoreScroll)();
167
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]));
168
196
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
169
197
  ref: containerRef,
170
- 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",
171
199
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Container, {
172
200
  maxWidth: "6xl",
173
201
  children: [
@@ -180,68 +208,77 @@
180
208
  children: [additionalActions, pluginActions]
181
209
  }),
182
210
  additionalChildrenStart,
183
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
184
- className: "flex flex-col gap-8 pt-2",
185
- children: SECTIONS.map((section) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
186
- "aria-label": section.label,
187
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
188
- variant: "caption",
189
- component: "h2",
190
- color: "secondary",
191
- className: "py-2 font-medium uppercase text-sm text-surface-600 dark:text-surface-400",
192
- children: section.label
193
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
194
- className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 mt-2",
195
- children: section.tools.map((tool) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Card, {
196
- onClick: () => {
197
- navigate(tool.path);
198
- context.analyticsController?.onAnalyticsEvent?.("home_navigate_to_view", { path: tool.path });
199
- },
200
- 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"),
201
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
202
- className: "flex flex-col items-start h-full",
203
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
204
- className: "grow w-full",
205
- children: [
206
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
207
- className: (0, _rebasepro_ui.cls)("h-6 flex items-center", section.iconColor),
208
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_core.IconForView, {
209
- collectionOrView: {
210
- slug: tool.path,
211
- name: tool.name,
212
- icon: tool.icon
213
- },
214
- size: "small"
215
- })
216
- }),
217
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
218
- gutterBottom: true,
219
- variant: "subtitle1",
220
- className: "mt-1 font-semibold",
221
- component: "h2",
222
- children: tool.name
223
- }),
224
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
225
- variant: "caption",
226
- color: "secondary",
227
- component: "div",
228
- children: tool.description
229
- })
230
- ]
231
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
232
- style: { alignSelf: "flex-end" },
233
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
234
- className: "p-2",
235
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.ArrowRightIcon, {
236
- className: "text-primary",
237
- size: _rebasepro_ui.iconSize.small
238
- })
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
+ ]
239
276
  })
240
- })]
277
+ }, tool.path))
241
278
  })
242
- }, tool.path))
243
- })]
244
- }, section.label))
279
+ })
280
+ })
281
+ }, section.label);
245
282
  }),
246
283
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
247
284
  className: "mt-10 mb-6",
@@ -252,7 +289,7 @@
252
289
  variant: "caption",
253
290
  component: "h2",
254
291
  color: "secondary",
255
- 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"),
256
293
  children: "Quick Start"
257
294
  })
258
295
  }),
@@ -305,7 +342,7 @@
305
342
  variant: "caption",
306
343
  component: "h2",
307
344
  color: "secondary",
308
- 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"),
309
346
  children: s.title
310
347
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
311
348
  className: "mt-4",
@@ -896,7 +933,7 @@
896
933
  }) : Object.entries(schemas).map(([schemaName, tables]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
897
934
  className: "mb-2",
898
935
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
899
- className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors",
936
+ className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
900
937
  onClick: () => setExpandedSchemas((prev) => ({
901
938
  ...prev,
902
939
  [schemaName]: !prev[schemaName]
@@ -914,7 +951,7 @@
914
951
  }), expandedSchemas[schemaName] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
915
952
  className: "ml-3 mt-1 space-y-1",
916
953
  children: tables.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
917
- className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors group relative",
954
+ className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative",
918
955
  onClick: () => setExpandedTables((prev) => ({
919
956
  ...prev,
920
957
  [`${schemaName}.${table.tableName}`]: !prev[`${schemaName}.${table.tableName}`]
@@ -1017,7 +1054,7 @@
1017
1054
  }), expandedTables[`${schemaName}.${table.tableName}`] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1018
1055
  className: (0, _rebasepro_ui.cls)("ml-5 mt-1 space-y-0.5 border-l", _rebasepro_ui.defaultBorderMixin),
1019
1056
  children: table.columns.map((col) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1020
- className: "flex items-center p-1 group pl-2 hover:bg-surface-50 dark:hover:bg-surface-950 rounded-r relative min-h-[28px]",
1057
+ className: "flex items-center p-1 group pl-2 hover:bg-surface-50 dark:hover:bg-surface-900 rounded-r relative min-h-[28px]",
1021
1058
  children: [
1022
1059
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1023
1060
  className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0",
@@ -2856,10 +2893,10 @@ interface FindResponse<M extends Record<string, any> = any> {
2856
2893
  };
2857
2894
  }
2858
2895
 
2859
- type FilterOperator = "=" | "!=" | ">" | ">=" | "<" | "<=" | "in" | "not-in" | "array-contains" | "array-contains-any" | "is" | "is_not" | "like" | "ilike";
2896
+ type WhereFilterOp = "<" | "<=" | "==" | "!=" | ">=" | ">" | "in" | "not-in" | "array-contains" | "array-contains-any";
2860
2897
 
2861
2898
  interface QueryBuilder<M extends Record<string, any> = any> {
2862
- where(column: keyof M & string, operator: FilterOperator, value: any): QueryBuilder<M>;
2899
+ where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;
2863
2900
  orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilder<M>;
2864
2901
  limit(count: number): QueryBuilder<M>;
2865
2902
  offset(count: number): QueryBuilder<M>;
@@ -2875,7 +2912,7 @@ interface CollectionClient<M extends Record<string, any> = any> {
2875
2912
  create(data: Partial<M>, id?: string | number): Promise<Entity<M>>;
2876
2913
  update(id: string | number, data: Partial<M>): Promise<Entity<M>>;
2877
2914
  delete(id: string | number): Promise<void>;
2878
- where(column: keyof M & string, operator: FilterOperator, value: any): QueryBuilder<M>;
2915
+ where(column: keyof M & string, operator: WhereFilterOp, value: any): QueryBuilder<M>;
2879
2916
  orderBy(column: keyof M & string, direction?: "asc" | "desc"): QueryBuilder<M>;
2880
2917
  limit(count: number): QueryBuilder<M>;
2881
2918
  offset(count: number): QueryBuilder<M>;
@@ -3130,7 +3167,7 @@ declare const context: JSEditorContext;
3130
3167
  function CollectionItem({ collection, onInsertCode }) {
3131
3168
  const [expanded, setExpanded] = (0, react.useState)(false);
3132
3169
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3133
- className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors group relative",
3170
+ className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative",
3134
3171
  onClick: () => setExpanded(!expanded),
3135
3172
  children: [
3136
3173
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -3172,7 +3209,7 @@ declare const context: JSEditorContext;
3172
3209
  }), expanded && collection.properties.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3173
3210
  className: (0, _rebasepro_ui.cls)("ml-5 mt-1 space-y-0.5 border-l", _rebasepro_ui.defaultBorderMixin),
3174
3211
  children: collection.properties.map((prop) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3175
- className: "flex items-center p-1 pl-2 hover:bg-surface-50 dark:hover:bg-surface-950 rounded-r cursor-pointer transition-colors group/prop relative min-h-[28px]",
3212
+ className: "flex items-center p-1 pl-2 hover:bg-surface-50 dark:hover:bg-surface-900 rounded-r cursor-pointer transition-colors group/prop relative min-h-[28px]",
3176
3213
  onClick: () => onInsertCode(`"${prop}"`),
3177
3214
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3178
3215
  className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0",
@@ -3455,7 +3492,7 @@ declare const context: JSEditorContext;
3455
3492
  children: "No execution history yet"
3456
3493
  })
3457
3494
  }) : [...history].reverse().map((code, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
3458
- className: "p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-950 cursor-pointer group transition-colors flex items-start",
3495
+ className: "p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-900 cursor-pointer group transition-colors flex items-start",
3459
3496
  onClick: () => onSelectSnippet(code),
3460
3497
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3461
3498
  className: "w-3 h-3 mt-1 mr-2 text-text-disabled dark:text-text-disabled-dark group-hover:text-primary transition-colors flex-shrink-0",
@@ -5021,6 +5058,12 @@ return result;
5021
5058
  //#endregion
5022
5059
  //#region src/components/RLSEditor/RLSEditor.tsx
5023
5060
  var RLSEditor_exports = /* @__PURE__ */ __exportAll({ RLSEditor: () => RLSEditor$1 });
5061
+ function classifyTableClient(tableName, schemaName, junctionTableNames, isMappedToCollection) {
5062
+ if (_rebasepro_common.REBASE_INTERNAL_SCHEMAS.includes(schemaName) || _rebasepro_common.REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))) return "internal";
5063
+ if (isMappedToCollection) return "collection";
5064
+ if (junctionTableNames.has(tableName)) return "junction";
5065
+ return "other";
5066
+ }
5024
5067
  /**
5025
5068
  * Validates and double-quotes a SQL identifier to prevent injection.
5026
5069
  * Only allows safe Postgres identifiers (letters, digits, underscores).
@@ -5030,6 +5073,81 @@ return result;
5030
5073
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) throw new Error(`Invalid SQL identifier: "${name}". Only letters, digits, and underscores are allowed.`);
5031
5074
  return `"${name}"`;
5032
5075
  }
5076
+ function SidebarSection({ title, icon, expanded, onToggle, count, children }) {
5077
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5078
+ className: "mb-2",
5079
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5080
+ className: "flex items-center p-1.5 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
5081
+ onClick: onToggle,
5082
+ children: [
5083
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5084
+ className: (0, _rebasepro_ui.cls)("w-3 h-3 mr-1.5 transition-transform text-text-disabled dark:text-text-disabled-dark", expanded ? "rotate-90" : ""),
5085
+ fill: "currentColor",
5086
+ viewBox: "0 0 20 20",
5087
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" })
5088
+ }),
5089
+ icon,
5090
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5091
+ variant: "body2",
5092
+ className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow ml-1.5",
5093
+ children: title
5094
+ }),
5095
+ count !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5096
+ className: "text-[10px] text-text-disabled dark:text-text-disabled-dark font-medium tabular-nums mr-1",
5097
+ children: count
5098
+ })
5099
+ ]
5100
+ }), expanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5101
+ className: "ml-3 mt-0.5 space-y-0.5",
5102
+ children
5103
+ })]
5104
+ });
5105
+ }
5106
+ function SidebarTableRow({ table, isSelected, onSelect, badge, dimmed, t }) {
5107
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5108
+ onClick: onSelect,
5109
+ className: (0, _rebasepro_ui.cls)("flex items-center p-1 cursor-pointer rounded transition-colors group relative", isSelected ? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light" : "hover:bg-surface-100 dark:hover:bg-surface-900 text-text-secondary dark:text-text-secondary-dark", dimmed && !isSelected && "opacity-60"),
5110
+ children: [
5111
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5112
+ className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
5113
+ fill: "none",
5114
+ stroke: "currentColor",
5115
+ viewBox: "0 0 24 24",
5116
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
5117
+ strokeLinecap: "round",
5118
+ strokeLinejoin: "round",
5119
+ strokeWidth: 2,
5120
+ d: "M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
5121
+ })
5122
+ }),
5123
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5124
+ variant: "body2",
5125
+ className: "text-xs truncate flex-1 min-w-0",
5126
+ children: table.tableName
5127
+ }),
5128
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5129
+ className: "flex items-center gap-1.5 shrink-0 ml-2",
5130
+ children: [
5131
+ badge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5132
+ className: "text-[9px] uppercase tracking-wider font-semibold text-text-disabled dark:text-text-disabled-dark bg-surface-200 dark:bg-surface-800 rounded px-1 py-px",
5133
+ children: badge
5134
+ }),
5135
+ table.rlsEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5136
+ title: t("studio_rls_enabled"),
5137
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
5138
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5139
+ title: t("studio_rls_disabled"),
5140
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
5141
+ }),
5142
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5143
+ className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
5144
+ children: table.policies.length
5145
+ })
5146
+ ]
5147
+ })
5148
+ ]
5149
+ });
5150
+ }
5033
5151
  var RLSEditor$1;
5034
5152
  var init_RLSEditor = __esmMin((() => {
5035
5153
  init_PolicyEditor();
@@ -5043,6 +5161,7 @@ return result;
5043
5161
  const [tables, setTables] = (0, react.useState)([]);
5044
5162
  const [selectedTable, setSelectedTable] = (0, react.useState)(null);
5045
5163
  const [activeTab, setActiveTab] = (0, react.useState)(0);
5164
+ const [junctionTableNames, setJunctionTableNames] = (0, react.useState)(/* @__PURE__ */ new Set());
5046
5165
  const [editingPolicy, setEditingPolicy] = (0, react.useState)(null);
5047
5166
  const [sidebarSize, setSidebarSize] = (0, react.useState)(() => {
5048
5167
  try {
@@ -5052,7 +5171,11 @@ return result;
5052
5171
  return 20;
5053
5172
  }
5054
5173
  });
5055
- const [expandedSchemas, setExpandedSchemas] = (0, react.useState)({ public: true });
5174
+ const [expandedSections, setExpandedSections] = (0, react.useState)({
5175
+ collection: true,
5176
+ other: true,
5177
+ internal: false
5178
+ });
5056
5179
  const [sidebarTab, setSidebarTab] = (0, react.useState)("tables");
5057
5180
  (0, react.useEffect)(() => {
5058
5181
  try {
@@ -5134,7 +5257,15 @@ return result;
5134
5257
  });
5135
5258
  const sortedTables = Object.values(tableMap).sort((a, b) => a.tableName.localeCompare(b.tableName));
5136
5259
  setTables(sortedTables);
5137
- if (sortedTables.length > 0 && !selectedTable) setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
5260
+ try {
5261
+ const jRows = extractRows(await databaseAdmin.executeSql(_rebasepro_common.JUNCTION_TABLES_SQL));
5262
+ setJunctionTableNames(new Set(jRows.map((r) => r.table_name).filter(Boolean)));
5263
+ } catch {}
5264
+ if (sortedTables.length > 0 && !selectedTable) {
5265
+ const firstUserTable = sortedTables.find((t) => !_rebasepro_common.REBASE_INTERNAL_SCHEMAS.includes(t.schemaName));
5266
+ if (firstUserTable) setSelectedTable(`${firstUserTable.schemaName}.${firstUserTable.tableName}`);
5267
+ else setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
5268
+ }
5138
5269
  } catch (e) {
5139
5270
  console.error("RLS fetch error:", e);
5140
5271
  setError("Failed to fetch RLS policies: " + (e instanceof Error ? e.message : String(e)));
@@ -5152,18 +5283,38 @@ return result;
5152
5283
  if (!selectedTable) return null;
5153
5284
  return tables.find((t) => `${t.schemaName}.${t.tableName}` === selectedTable) || null;
5154
5285
  }, [selectedTable, tables]);
5155
- const groupedTables = (0, react.useMemo)(() => {
5156
- const groups = {};
5286
+ /** Categorize tables into 4 buckets for the sidebar. */
5287
+ const categorizedTables = (0, react.useMemo)(() => {
5288
+ const groups = {
5289
+ collection: [],
5290
+ junction: [],
5291
+ internal: [],
5292
+ other: []
5293
+ };
5157
5294
  tables.forEach((table) => {
5158
- if (!groups[table.schemaName]) groups[table.schemaName] = [];
5159
- groups[table.schemaName].push(table);
5295
+ const isMapped = !!collectionRegistry.collections?.find((c) => c.id === table.tableName || c.path === table.tableName || c.table === table.tableName || c.slug === table.tableName || c.collectionId === table.tableName);
5296
+ groups[classifyTableClient(table.tableName, table.schemaName, junctionTableNames, isMapped)].push(table);
5160
5297
  });
5161
5298
  return groups;
5162
- }, [tables]);
5299
+ }, [
5300
+ tables,
5301
+ junctionTableNames,
5302
+ collectionRegistry.collections
5303
+ ]);
5163
5304
  const activeCollection = (0, react.useMemo)(() => {
5164
5305
  if (!activeTableData) return null;
5165
5306
  return collectionRegistry.collections?.find((c) => c.id === activeTableData.tableName || c.path === activeTableData.tableName || c.table === activeTableData.tableName || c.slug === activeTableData.tableName || c.collectionId === activeTableData.tableName) || null;
5166
5307
  }, [activeTableData, collectionRegistry.collections]);
5308
+ /** The category of the currently selected table. */
5309
+ const activeTableCategory = (0, react.useMemo)(() => {
5310
+ if (!activeTableData) return null;
5311
+ const isMapped = !!activeCollection;
5312
+ return classifyTableClient(activeTableData.tableName, activeTableData.schemaName, junctionTableNames, isMapped);
5313
+ }, [
5314
+ activeTableData,
5315
+ activeCollection,
5316
+ junctionTableNames
5317
+ ]);
5167
5318
  const mergedPolicies = (0, react.useMemo)(() => {
5168
5319
  if (!activeTableData) return [];
5169
5320
  const policiesMap = {};
@@ -5261,74 +5412,78 @@ return result;
5261
5412
  children: isLoading && tables.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5262
5413
  className: "flex justify-center p-4",
5263
5414
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "small" })
5264
- }) : Object.keys(groupedTables).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5415
+ }) : tables.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5265
5416
  className: "p-4 text-center",
5266
5417
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5267
5418
  variant: "caption",
5268
5419
  className: "text-text-disabled dark:text-text-disabled-dark italic",
5269
5420
  children: t("studio_rls_no_tables")
5270
5421
  })
5271
- }) : Object.entries(groupedTables).map(([schemaName, schemaTables]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5272
- className: "mb-2",
5273
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5274
- className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors",
5275
- onClick: () => setExpandedSchemas((prev) => ({
5422
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5423
+ categorizedTables.collection.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSection, {
5424
+ title: "Schema Collections",
5425
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DatabaseIcon, {
5426
+ size: 12,
5427
+ className: "text-primary dark:text-primary-light"
5428
+ }),
5429
+ expanded: expandedSections.collection ?? true,
5430
+ onToggle: () => setExpandedSections((prev) => ({
5276
5431
  ...prev,
5277
- [schemaName]: !prev[schemaName]
5432
+ collection: !(prev.collection ?? true)
5278
5433
  })),
5279
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5280
- className: (0, _rebasepro_ui.cls)("w-3 h-3 mr-1 transition-transform", expandedSchemas[schemaName] ? "rotate-90" : ""),
5281
- fill: "currentColor",
5282
- viewBox: "0 0 20 20",
5283
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" })
5284
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5285
- variant: "body2",
5286
- className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow",
5287
- children: schemaName
5288
- })]
5289
- }), expandedSchemas[schemaName] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5290
- className: "ml-3 mt-1 space-y-0.5",
5291
- children: schemaTables.map((table) => {
5292
- const key = `${table.schemaName}.${table.tableName}`;
5293
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5294
- onClick: () => setSelectedTable(key),
5295
- className: (0, _rebasepro_ui.cls)("flex items-center p-1 cursor-pointer rounded transition-colors group relative", selectedTable === key ? "bg-primary/10 text-primary dark:bg-primary/20 dark:text-primary-light" : "hover:bg-surface-100 dark:hover:bg-surface-950 text-text-secondary dark:text-text-secondary-dark"),
5296
- children: [
5297
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5298
- className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
5299
- fill: "none",
5300
- stroke: "currentColor",
5301
- viewBox: "0 0 24 24",
5302
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
5303
- strokeLinecap: "round",
5304
- strokeLinejoin: "round",
5305
- strokeWidth: 2,
5306
- d: "M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
5307
- })
5308
- }),
5309
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5310
- variant: "body2",
5311
- className: "text-xs truncate flex-1 min-w-0",
5312
- children: table.tableName
5313
- }),
5314
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5315
- className: "flex items-center gap-1.5 shrink-0 ml-2",
5316
- children: [table.rlsEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5317
- title: t("studio_rls_enabled"),
5318
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
5319
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5320
- title: t("studio_rls_disabled"),
5321
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
5322
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5323
- className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
5324
- children: table.policies.length
5325
- })]
5326
- })
5327
- ]
5328
- }, key);
5329
- })
5330
- })]
5331
- }, schemaName))
5434
+ children: categorizedTables.collection.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5435
+ table,
5436
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5437
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5438
+ t
5439
+ }, `${table.schemaName}.${table.tableName}`))
5440
+ }),
5441
+ (categorizedTables.other.length > 0 || categorizedTables.junction.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SidebarSection, {
5442
+ title: "Other Tables",
5443
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertTriangleIcon, {
5444
+ size: 12,
5445
+ className: "text-yellow-500 dark:text-yellow-400"
5446
+ }),
5447
+ expanded: expandedSections.other ?? true,
5448
+ onToggle: () => setExpandedSections((prev) => ({
5449
+ ...prev,
5450
+ other: !(prev.other ?? true)
5451
+ })),
5452
+ children: [categorizedTables.other.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5453
+ table,
5454
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5455
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5456
+ t
5457
+ }, `${table.schemaName}.${table.tableName}`)), categorizedTables.junction.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5458
+ table,
5459
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5460
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5461
+ badge: "Junction",
5462
+ dimmed: true,
5463
+ t
5464
+ }, `${table.schemaName}.${table.tableName}`))]
5465
+ }),
5466
+ categorizedTables.internal.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSection, {
5467
+ title: "Rebase Internal",
5468
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.LockIcon, {
5469
+ size: 12,
5470
+ className: "text-text-disabled dark:text-text-disabled-dark"
5471
+ }),
5472
+ expanded: expandedSections.internal ?? false,
5473
+ onToggle: () => setExpandedSections((prev) => ({
5474
+ ...prev,
5475
+ internal: !(prev.internal ?? false)
5476
+ })),
5477
+ count: categorizedTables.internal.length,
5478
+ children: categorizedTables.internal.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5479
+ table,
5480
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5481
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5482
+ dimmed: true,
5483
+ t
5484
+ }, `${table.schemaName}.${table.tableName}`))
5485
+ })
5486
+ ] })
5332
5487
  })]
5333
5488
  }), sidebarTab === "info" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5334
5489
  className: "flex flex-col h-full",
@@ -5491,10 +5646,11 @@ return result;
5491
5646
  size: "small",
5492
5647
  onClick: async () => {
5493
5648
  const table = activeTableData.tableName;
5649
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
5494
5650
  const action = activeTableData.rlsEnabled ? "DISABLE" : "ENABLE";
5495
5651
  if (!confirm(`Are you sure you want to ${action.toLowerCase()} Row Level Security on "${table}"?`)) return;
5496
5652
  try {
5497
- await databaseAdmin.executeSql(`ALTER TABLE ${sanitizeSqlIdentifier(table)} ${action} ROW LEVEL SECURITY`);
5653
+ await databaseAdmin.executeSql(`ALTER TABLE ${qualifiedTable} ${action} ROW LEVEL SECURITY`);
5498
5654
  snackbarController.open({
5499
5655
  type: "success",
5500
5656
  message: `RLS ${action.toLowerCase()}d on ${table}`
@@ -5521,7 +5677,6 @@ return result;
5521
5677
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5522
5678
  size: "small",
5523
5679
  color: "primary",
5524
- disabled: !activeCollection,
5525
5680
  onClick: () => setEditingPolicy("new"),
5526
5681
  children: t("studio_rls_create_policy")
5527
5682
  })
@@ -5562,31 +5717,57 @@ return result;
5562
5717
  schema: activeTableData.schemaName,
5563
5718
  table: activeTableData.tableName,
5564
5719
  onSave: async (newPolicy) => {
5565
- if (!activeCollection) return;
5566
- const rule = {
5567
- name: newPolicy.policyname,
5568
- operation: newPolicy.cmd?.toLowerCase(),
5569
- mode: newPolicy.permissive?.toLowerCase(),
5570
- using: newPolicy.qual || void 0,
5571
- withCheck: newPolicy.with_check || void 0,
5572
- roles: newPolicy.roles
5573
- };
5574
- const existingRules = ((0, _rebasepro_types.isPostgresCollection)(activeCollection) ? activeCollection.securityRules : void 0) || [];
5575
- let newRules;
5576
- if (editingPolicy === "new") newRules = [...existingRules, rule];
5577
- else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
5578
- try {
5579
- if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
5580
- method: "POST",
5581
- headers: { "Content-Type": "application/json" },
5582
- body: JSON.stringify({
5583
- collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
5584
- collectionData: { securityRules: newRules }
5585
- })
5586
- })).ok) throw new Error("Failed to save policy");
5720
+ if (activeCollection) {
5721
+ const rule = {
5722
+ name: newPolicy.policyname,
5723
+ operation: newPolicy.cmd?.toLowerCase(),
5724
+ mode: newPolicy.permissive?.toLowerCase(),
5725
+ using: newPolicy.qual || void 0,
5726
+ withCheck: newPolicy.with_check || void 0,
5727
+ roles: newPolicy.roles
5728
+ };
5729
+ const existingRules = ((0, _rebasepro_types.isPostgresCollection)(activeCollection) ? activeCollection.securityRules : void 0) || [];
5730
+ let newRules;
5731
+ if (editingPolicy === "new") newRules = [...existingRules, rule];
5732
+ else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
5733
+ try {
5734
+ if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
5735
+ method: "POST",
5736
+ headers: { "Content-Type": "application/json" },
5737
+ body: JSON.stringify({
5738
+ collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
5739
+ collectionData: { securityRules: newRules }
5740
+ })
5741
+ })).ok) throw new Error("Failed to save policy");
5742
+ snackbarController.open({
5743
+ type: "success",
5744
+ message: "Policy saved successfully"
5745
+ });
5746
+ setEditingPolicy(null);
5747
+ fetchRLSData();
5748
+ } catch (e) {
5749
+ snackbarController.open({
5750
+ type: "error",
5751
+ message: e instanceof Error ? e.message : String(e)
5752
+ });
5753
+ }
5754
+ } else try {
5755
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(activeTableData.tableName)}`;
5756
+ const policyName = sanitizeSqlIdentifier(newPolicy.policyname || "unnamed_policy");
5757
+ const cmd = newPolicy.cmd || "ALL";
5758
+ const permissive = (newPolicy.permissive || "PERMISSIVE") === "PERMISSIVE" ? "PERMISSIVE" : "RESTRICTIVE";
5759
+ const roles = newPolicy.roles && newPolicy.roles.length > 0 ? newPolicy.roles.map((r) => sanitizeSqlIdentifier(r)).join(", ") : "public";
5760
+ if (editingPolicy !== "new") await databaseAdmin.executeSql(`DROP POLICY IF EXISTS ${policyName} ON ${qualifiedTable}`);
5761
+ let sql = `CREATE POLICY ${policyName} ON ${qualifiedTable}`;
5762
+ sql += ` AS ${permissive}`;
5763
+ sql += ` FOR ${cmd}`;
5764
+ sql += ` TO ${roles}`;
5765
+ if (newPolicy.qual) sql += ` USING (${newPolicy.qual})`;
5766
+ if (newPolicy.with_check) sql += ` WITH CHECK (${newPolicy.with_check})`;
5767
+ await databaseAdmin.executeSql(sql);
5587
5768
  snackbarController.open({
5588
5769
  type: "success",
5589
- message: "Policy saved successfully"
5770
+ message: `Policy "${newPolicy.policyname}" applied directly via SQL`
5590
5771
  });
5591
5772
  setEditingPolicy(null);
5592
5773
  fetchRLSData();
@@ -5605,17 +5786,59 @@ return result;
5605
5786
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5606
5787
  className: "max-w-4xl mx-auto flex flex-col gap-6",
5607
5788
  children: [
5608
- activeTableData && !activeCollection && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Alert, {
5789
+ activeTableData && activeTableCategory === "internal" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
5790
+ color: "info",
5791
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5792
+ className: "flex items-start gap-2",
5793
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.LockIcon, {
5794
+ size: 16,
5795
+ className: "shrink-0 mt-0.5"
5796
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5797
+ variant: "body2",
5798
+ className: "mb-1 font-semibold",
5799
+ children: "Rebase System Table"
5800
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5801
+ variant: "caption",
5802
+ className: "opacity-80",
5803
+ children: "This table is managed internally by Rebase. Its security policies are configured automatically. Editing policies on system tables is an advanced operation."
5804
+ })] })]
5805
+ })
5806
+ }),
5807
+ activeTableData && activeTableCategory === "junction" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
5808
+ color: "info",
5809
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5810
+ className: "flex items-start gap-2",
5811
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Link2Icon, {
5812
+ size: 16,
5813
+ className: "shrink-0 mt-0.5"
5814
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5815
+ variant: "body2",
5816
+ className: "mb-1 font-semibold",
5817
+ children: "Junction Table"
5818
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5819
+ variant: "caption",
5820
+ className: "opacity-80",
5821
+ children: "This is an auto-generated junction table for a many-to-many relation. Its access is typically managed through the related collections' policies. You can still add RLS policies directly if needed."
5822
+ })] })]
5823
+ })
5824
+ }),
5825
+ activeTableData && activeTableCategory === "other" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
5609
5826
  color: "warning",
5610
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5611
- variant: "body2",
5612
- className: "mb-1",
5613
- children: "Table not managed by Rebase"
5614
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5615
- variant: "caption",
5616
- className: "opacity-80",
5617
- children: "This table is not mapped to a Rebase Schema via code. To edit security policies visually, you must first import this table into a Schema configuration file."
5618
- })]
5827
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5828
+ className: "flex items-start gap-2",
5829
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertTriangleIcon, {
5830
+ size: 16,
5831
+ className: "shrink-0 mt-0.5"
5832
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5833
+ variant: "body2",
5834
+ className: "mb-1 font-semibold",
5835
+ children: "Unmapped Table"
5836
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5837
+ variant: "caption",
5838
+ className: "opacity-80",
5839
+ children: "This table exists in the database but isn't mapped to a collection definition. Import it into a Schema configuration file to manage security policies visually."
5840
+ })] })]
5841
+ })
5619
5842
  }),
5620
5843
  activeTableData && !activeTableData.rlsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5621
5844
  className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-900/50 rounded-lg flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between", _rebasepro_ui.defaultBorderMixin),
@@ -5729,7 +5952,6 @@ return result;
5729
5952
  variant: "text",
5730
5953
  color: "primary",
5731
5954
  onClick: () => setEditingPolicy(policy),
5732
- disabled: !activeCollection,
5733
5955
  children: t("studio_rls_edit")
5734
5956
  }),
5735
5957
  policy.status !== "code_only" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
@@ -5739,9 +5961,10 @@ return result;
5739
5961
  size: "small",
5740
5962
  onClick: async () => {
5741
5963
  const table = activeTableData.tableName;
5964
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
5742
5965
  if (!confirm(`Drop policy "${policy.policyname}" from table "${table}"?`)) return;
5743
5966
  try {
5744
- await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${sanitizeSqlIdentifier(table)}`);
5967
+ await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${qualifiedTable}`);
5745
5968
  snackbarController.open({
5746
5969
  type: "success",
5747
5970
  message: `Policy "${policy.policyname}" dropped`
@@ -5778,7 +6001,7 @@ return result;
5778
6001
  className: "text-text-disabled dark:text-text-disabled-dark max-w-sm mb-4",
5779
6002
  children: "RLS is enabled on this table but no policies exist. All access is denied by default (Postgres deny-all). Create a policy to allow specific access."
5780
6003
  }),
5781
- activeCollection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
6004
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5782
6005
  size: "small",
5783
6006
  variant: "filled",
5784
6007
  color: "primary",
@@ -6223,8 +6446,16 @@ return result;
6223
6446
  var StorageView$1;
6224
6447
  var init_StorageView = __esmMin((() => {
6225
6448
  StorageView$1 = () => {
6226
- const storageSource = (0, _rebasepro_core.useStorageSource)();
6449
+ const defaultStorageSource = (0, _rebasepro_core.useStorageSource)();
6450
+ const storageSources = (0, _rebasepro_core.useStorageSources)();
6227
6451
  const snackbarController = (0, _rebasepro_core.useSnackbarController)();
6452
+ const sourceKeys = (0, react.useMemo)(() => {
6453
+ const keys = Object.keys(storageSources.sources);
6454
+ if (!keys.includes(_rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY)) keys.unshift(_rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY);
6455
+ return keys;
6456
+ }, [storageSources.sources]);
6457
+ const [selectedSourceKey, setSelectedSourceKey] = (0, react.useState)(_rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY);
6458
+ const storageSource = storageSources.sources[selectedSourceKey] ?? defaultStorageSource;
6228
6459
  const [searchParams, setSearchParams] = (0, react_router_dom.useSearchParams)();
6229
6460
  const currentPath = searchParams.get("path") || "";
6230
6461
  const [loading, setLoading] = (0, react.useState)(true);
@@ -6288,7 +6519,11 @@ return result;
6288
6519
  }, []);
6289
6520
  (0, react.useEffect)(() => {
6290
6521
  fetchContents(currentPath);
6291
- }, [currentPath, fetchContents]);
6522
+ }, [
6523
+ currentPath,
6524
+ fetchContents,
6525
+ selectedSourceKey
6526
+ ]);
6292
6527
  const handleNavigate = (0, react.useCallback)((path) => {
6293
6528
  if (!path) setSearchParams({});
6294
6529
  else setSearchParams({ path });
@@ -6968,6 +7203,21 @@ return result;
6968
7203
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6969
7204
  className: "flex shrink-0 items-center justify-end gap-1.5 pr-1",
6970
7205
  children: [
7206
+ sourceKeys.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Select, {
7207
+ size: "small",
7208
+ position: "item-aligned",
7209
+ value: selectedSourceKey,
7210
+ onValueChange: (value) => {
7211
+ if (value) setSelectedSourceKey(value);
7212
+ },
7213
+ renderValue: (key) => {
7214
+ return storageSources.registry[key]?.label ?? (key === _rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY ? "Default" : key);
7215
+ },
7216
+ children: sourceKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
7217
+ value: key,
7218
+ children: storageSources.registry[key]?.label ?? (key === _rebasepro_types.DEFAULT_STORAGE_SOURCE_KEY ? "Default" : key)
7219
+ }, key))
7220
+ }),
6971
7221
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
6972
7222
  title: "Grid view",
6973
7223
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
@@ -7213,7 +7463,7 @@ return result;
7213
7463
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
7214
7464
  return `${(ms / 6e4).toFixed(1)}m`;
7215
7465
  }
7216
- function formatRelative$1(iso) {
7466
+ function formatRelative$2(iso) {
7217
7467
  if (!iso) return "—";
7218
7468
  const d = new Date(iso);
7219
7469
  const now = Date.now();
@@ -7501,20 +7751,20 @@ return result;
7501
7751
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7502
7752
  className: "grid grid-cols-2 md:grid-cols-4 gap-3",
7503
7753
  children: [
7504
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7754
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7505
7755
  label: "Schedule",
7506
7756
  value: selectedJob.schedule,
7507
7757
  mono: true
7508
7758
  }),
7509
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7759
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7510
7760
  label: "Last Run",
7511
- value: formatRelative$1(selectedJob.lastRunAt)
7761
+ value: formatRelative$2(selectedJob.lastRunAt)
7512
7762
  }),
7513
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7763
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7514
7764
  label: "Next Run",
7515
- value: selectedJob.enabled ? formatRelative$1(selectedJob.nextRunAt) : "Paused"
7765
+ value: selectedJob.enabled ? formatRelative$2(selectedJob.nextRunAt) : "Paused"
7516
7766
  }),
7517
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7767
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7518
7768
  label: "Duration",
7519
7769
  value: selectedJob.lastDurationMs !== void 0 ? formatDuration(selectedJob.lastDurationMs) : "—"
7520
7770
  })
@@ -7523,16 +7773,16 @@ return result;
7523
7773
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7524
7774
  className: "grid grid-cols-3 gap-3 mt-3",
7525
7775
  children: [
7526
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7527
7777
  label: "Status",
7528
7778
  value: selectedJob.state.toUpperCase(),
7529
7779
  chipColor: selectedJob.state === "error" ? "red" : selectedJob.state === "disabled" ? "gray" : "green"
7530
7780
  }),
7531
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7781
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7532
7782
  label: "Total Runs",
7533
7783
  value: String(selectedJob.totalRuns)
7534
7784
  }),
7535
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7785
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7536
7786
  label: "Failures",
7537
7787
  value: String(selectedJob.totalFailures),
7538
7788
  highlight: selectedJob.totalFailures > 0
@@ -7599,7 +7849,7 @@ return result;
7599
7849
  })]
7600
7850
  });
7601
7851
  }
7602
- function StatCard$1({ label, value, mono, chipColor, highlight }) {
7852
+ function StatCard$2({ label, value, mono, chipColor, highlight }) {
7603
7853
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7604
7854
  className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
7605
7855
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
@@ -8756,7 +9006,7 @@ return result;
8756
9006
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
8757
9007
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
8758
9008
  }
8759
- function formatRelative(date) {
9009
+ function formatRelative$1(date) {
8760
9010
  if (!date) return "—";
8761
9011
  const d = date instanceof Date ? date : new Date(date);
8762
9012
  if (isNaN(d.getTime())) return "—";
@@ -8958,7 +9208,7 @@ return result;
8958
9208
  "from ",
8959
9209
  branch.parentDatabase,
8960
9210
  " · ",
8961
- formatRelative(branch.createdAt)
9211
+ formatRelative$1(branch.createdAt)
8962
9212
  ]
8963
9213
  })]
8964
9214
  }),
@@ -9022,21 +9272,21 @@ return result;
9022
9272
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9023
9273
  className: "grid grid-cols-2 md:grid-cols-4 gap-3",
9024
9274
  children: [
9025
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9275
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9026
9276
  label: "Branch Name",
9027
9277
  value: selected.name,
9028
9278
  mono: true
9029
9279
  }),
9030
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9280
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9031
9281
  label: "Source Database",
9032
9282
  value: selected.parentDatabase,
9033
9283
  mono: true
9034
9284
  }),
9035
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9285
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9036
9286
  label: "Created",
9037
- value: formatRelative(selected.createdAt)
9287
+ value: formatRelative$1(selected.createdAt)
9038
9288
  }),
9039
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9289
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9040
9290
  label: "Size",
9041
9291
  value: formatSize(selected.sizeBytes)
9042
9292
  })
@@ -9138,13 +9388,12 @@ return result;
9138
9388
  }) }),
9139
9389
  branches.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
9140
9390
  label: "Source Database",
9141
- value: sourceBranch ?? "",
9142
- onValueChange: (v) => setSourceBranch(v || void 0),
9391
+ value: sourceBranch ?? "__main__",
9392
+ onValueChange: (v) => setSourceBranch(v === "__main__" ? void 0 : v),
9143
9393
  placeholder: "Default (main database)",
9144
9394
  size: "small",
9145
- renderValue: (v) => v || "Default (main database)",
9146
9395
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
9147
- value: "",
9396
+ value: "__main__",
9148
9397
  children: "Default (main database)"
9149
9398
  }), branches.map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
9150
9399
  value: b.name,
@@ -9185,7 +9434,7 @@ return result;
9185
9434
  ]
9186
9435
  });
9187
9436
  }
9188
- function StatCard({ label, value, mono }) {
9437
+ function StatCard$1({ label, value, mono }) {
9189
9438
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9190
9439
  className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
9191
9440
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
@@ -10174,16 +10423,16 @@ return result;
10174
10423
  var LogsExplorer_exports = /* @__PURE__ */ __exportAll({ LogsExplorer: () => LogsExplorer$1 });
10175
10424
  function LogsExplorer$1() {
10176
10425
  const [logs, setLogs] = (0, react.useState)([]);
10177
- const [level, setLevel] = (0, react.useState)("");
10178
- const [source, setSource] = (0, react.useState)("");
10426
+ const [level, setLevel] = (0, react.useState)("all");
10427
+ const [source, setSource] = (0, react.useState)("all");
10179
10428
  const [search, setSearch] = (0, react.useState)("");
10180
10429
  const [autoScroll, setAutoScroll] = (0, react.useState)(true);
10181
10430
  const containerRef = (0, react.useRef)(null);
10182
10431
  const fetchLogs = (0, react.useCallback)(async () => {
10183
10432
  try {
10184
10433
  const params = new URLSearchParams();
10185
- if (level) params.set("level", level);
10186
- if (source) params.set("source", source);
10434
+ if (level && level !== "all") params.set("level", level);
10435
+ if (source && source !== "all") params.set("source", source);
10187
10436
  if (search) params.set("search", search);
10188
10437
  params.set("limit", "200");
10189
10438
  const resp = await fetch(`/api/logs?${params}`);
@@ -10220,22 +10469,9 @@ return result;
10220
10469
  if (autoScroll && containerRef.current) containerRef.current.scrollTop = containerRef.current.scrollHeight;
10221
10470
  }, [logs, autoScroll]);
10222
10471
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10223
- style: {
10224
- display: "flex",
10225
- flexDirection: "column",
10226
- height: "calc(100vh - 64px)",
10227
- background: "#1e1e2e",
10228
- color: "#cdd6f4"
10229
- },
10472
+ className: "flex flex-col h-[calc(100vh-64px)] w-full bg-surface-50 dark:bg-surface-800",
10230
10473
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10231
- style: {
10232
- display: "flex",
10233
- gap: 8,
10234
- padding: "8px 16px",
10235
- borderBottom: "1px solid #313244",
10236
- alignItems: "center",
10237
- flexWrap: "wrap"
10238
- },
10474
+ className: (0, _rebasepro_ui.cls)("flex gap-2 p-3 border-b items-center flex-wrap shrink-0", _rebasepro_ui.defaultBorderMixin),
10239
10475
  children: [
10240
10476
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
10241
10477
  value: level,
@@ -10244,7 +10480,7 @@ return result;
10244
10480
  placeholder: "All Levels",
10245
10481
  children: [
10246
10482
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
10247
- value: "",
10483
+ value: "all",
10248
10484
  children: "All Levels"
10249
10485
  }),
10250
10486
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
@@ -10272,7 +10508,7 @@ return result;
10272
10508
  placeholder: "All Sources",
10273
10509
  children: [
10274
10510
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
10275
- value: "",
10511
+ value: "all",
10276
10512
  children: "All Sources"
10277
10513
  }),
10278
10514
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
@@ -10305,7 +10541,7 @@ return result;
10305
10541
  className: "flex-1 min-w-[200px]"
10306
10542
  }),
10307
10543
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10308
- className: "flex items-center gap-1.5 cursor-pointer",
10544
+ className: "flex items-center gap-1.5 cursor-pointer ml-2",
10309
10545
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
10310
10546
  id: "auto-scroll",
10311
10547
  checked: autoScroll,
@@ -10314,79 +10550,58 @@ return result;
10314
10550
  padding: false
10315
10551
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Label, {
10316
10552
  htmlFor: "auto-scroll",
10317
- className: "text-xs select-none cursor-pointer",
10553
+ className: "text-xs select-none cursor-pointer text-surface-600 dark:text-surface-400",
10318
10554
  children: "Auto-scroll"
10319
10555
  })]
10320
10556
  }),
10321
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10322
- style: {
10323
- fontSize: 12,
10324
- color: "#6c7086"
10325
- },
10326
- children: [logs.length, " entries"]
10557
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10558
+ className: "ml-auto pl-4",
10559
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10560
+ variant: "caption",
10561
+ color: "secondary",
10562
+ children: [logs.length, " entries"]
10563
+ })
10327
10564
  })
10328
10565
  ]
10329
10566
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10330
10567
  ref: containerRef,
10331
- style: {
10332
- flex: 1,
10333
- overflow: "auto",
10334
- fontFamily: "monospace",
10335
- fontSize: 12,
10336
- padding: "8px 0"
10337
- },
10568
+ className: "flex-1 overflow-auto py-2",
10338
10569
  children: [logs.map((log) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10339
- style: {
10340
- padding: "2px 16px",
10341
- display: "flex",
10342
- gap: 8,
10343
- borderBottom: "1px solid #181825"
10344
- },
10570
+ className: (0, _rebasepro_ui.cls)("flex gap-4 px-4 py-[6px] border-b hover:bg-surface-100 dark:hover:bg-surface-900 transition-colors", _rebasepro_ui.defaultBorderMixin),
10345
10571
  children: [
10346
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10347
- style: {
10348
- color: "#6c7086",
10349
- flexShrink: 0
10350
- },
10572
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10573
+ variant: "body2",
10574
+ color: "secondary",
10575
+ className: "w-[72px] shrink-0 font-mono",
10351
10576
  children: new Date(log.timestamp).toLocaleTimeString()
10352
10577
  }),
10353
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10354
- style: {
10355
- color: LEVEL_COLORS[log.level] || "#cdd6f4",
10356
- width: 40,
10357
- flexShrink: 0,
10358
- textTransform: "uppercase",
10359
- fontWeight: 600
10360
- },
10578
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10579
+ variant: "body2",
10580
+ className: (0, _rebasepro_ui.cls)("w-[48px] shrink-0 uppercase font-semibold font-mono", LEVEL_COLORS[log.level] || "text-surface-500"),
10361
10581
  children: log.level
10362
10582
  }),
10363
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10364
- style: {
10365
- color: SOURCE_COLORS[log.source] || "#cdd6f4",
10366
- width: 64,
10367
- flexShrink: 0
10368
- },
10583
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10584
+ variant: "body2",
10585
+ className: (0, _rebasepro_ui.cls)("w-[80px] shrink-0 font-mono", SOURCE_COLORS[log.source] || "text-surface-500"),
10369
10586
  children: [
10370
10587
  "[",
10371
10588
  log.source,
10372
10589
  "]"
10373
10590
  ]
10374
10591
  }),
10375
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10376
- style: {
10377
- color: "#cdd6f4",
10378
- flex: 1
10379
- },
10592
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10593
+ variant: "body2",
10594
+ className: "flex-1 font-mono break-all whitespace-pre-wrap text-surface-900 dark:text-surface-100",
10380
10595
  children: log.message
10381
10596
  })
10382
10597
  ]
10383
10598
  }, log.id)), logs.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10384
- style: {
10385
- padding: 32,
10386
- textAlign: "center",
10387
- color: "#6c7086"
10388
- },
10389
- children: "No log entries yet. Logs will appear here as requests come in."
10599
+ className: "p-8 text-center",
10600
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10601
+ variant: "body2",
10602
+ color: "secondary",
10603
+ children: "No log entries yet. Logs will appear here as requests come in."
10604
+ })
10390
10605
  })]
10391
10606
  })]
10392
10607
  });
@@ -10394,30 +10609,710 @@ return result;
10394
10609
  var LEVEL_COLORS, SOURCE_COLORS;
10395
10610
  var init_LogsExplorer = __esmMin((() => {
10396
10611
  LEVEL_COLORS = {
10397
- debug: "#6c7086",
10398
- info: "#89b4fa",
10399
- warn: "#f9e2af",
10400
- error: "#f38ba8"
10612
+ debug: "text-surface-500",
10613
+ info: "text-blue-600 dark:text-blue-500",
10614
+ warn: "text-amber-600 dark:text-amber-500",
10615
+ error: "text-red-600 dark:text-red-500"
10401
10616
  };
10402
10617
  SOURCE_COLORS = {
10403
- api: "#74c7ec",
10404
- auth: "#cba6f7",
10405
- storage: "#a6e3a1",
10406
- realtime: "#fab387",
10407
- system: "#6c7086"
10618
+ api: "text-sky-600 dark:text-sky-400",
10619
+ auth: "text-purple-600 dark:text-purple-400",
10620
+ storage: "text-green-600 dark:text-green-500",
10621
+ realtime: "text-orange-600 dark:text-orange-400",
10622
+ system: "text-surface-600 dark:text-surface-400"
10408
10623
  };
10409
10624
  }));
10410
10625
  //#endregion
10411
- //#region src/components/RebaseStudio.tsx
10412
- var SQLEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_SQLEditor(), SQLEditor_exports)).then((m) => ({ default: m.SQLEditor })));
10413
- var JSEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_JSEditor(), JSEditor_exports)).then((m) => ({ default: m.JSEditor })));
10414
- var RLSEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_RLSEditor(), RLSEditor_exports)).then((m) => ({ default: m.RLSEditor })));
10415
- var StorageView = (0, react.lazy)(() => Promise.resolve().then(() => (init_StorageView(), StorageView_exports)).then((m) => ({ default: m.StorageView })));
10416
- var CronJobsView = (0, react.lazy)(() => Promise.resolve().then(() => (init_CronJobsView(), CronJobsView_exports)).then((m) => ({ default: m.CronJobsView })));
10417
- var SchemaVisualizer = (0, react.lazy)(() => Promise.resolve().then(() => (init_SchemaVisualizer(), SchemaVisualizer_exports)).then((m) => ({ default: m.SchemaVisualizer })));
10418
- var BranchesView = (0, react.lazy)(() => Promise.resolve().then(() => (init_BranchesView(), BranchesView_exports)).then((m) => ({ default: m.BranchesView })));
10626
+ //#region src/components/ApiKeys/ApiKeysView.tsx
10627
+ var ApiKeysView_exports = /* @__PURE__ */ __exportAll({ ApiKeysView: () => ApiKeysView$1 });
10628
+ function formatRelative(iso) {
10629
+ if (!iso) return "—";
10630
+ const d = new Date(iso);
10631
+ const now = Date.now();
10632
+ const diff = d.getTime() - now;
10633
+ const abs = Math.abs(diff);
10634
+ if (abs < 6e4) return diff > 0 ? "in <1m" : "<1m ago";
10635
+ if (abs < 36e5) {
10636
+ const m = Math.round(abs / 6e4);
10637
+ return diff > 0 ? `in ${m}m` : `${m}m ago`;
10638
+ }
10639
+ if (abs < 864e5) {
10640
+ const h = Math.round(abs / 36e5);
10641
+ return diff > 0 ? `in ${h}h` : `${h}h ago`;
10642
+ }
10643
+ return d.toLocaleDateString();
10644
+ }
10645
+ function permissionSummary(perms) {
10646
+ if (perms.length === 0) return "No permissions";
10647
+ const wildcard = perms.find((p) => p.collection === "*");
10648
+ if (wildcard) return `All collections (${wildcard.operations.join(", ")})`;
10649
+ if (perms.length === 1) return `${perms[0].collection} (${perms[0].operations.join(", ")})`;
10650
+ return `${perms.length} collections`;
10651
+ }
10652
+ function isExpired(key) {
10653
+ return !!(key.expires_at && new Date(key.expires_at) < /* @__PURE__ */ new Date());
10654
+ }
10655
+ function keyStatus(key) {
10656
+ if (key.revoked_at) return {
10657
+ label: "Revoked",
10658
+ color: "text-red-500"
10659
+ };
10660
+ if (isExpired(key)) return {
10661
+ label: "Expired",
10662
+ color: "text-amber-500"
10663
+ };
10664
+ return {
10665
+ label: "Active",
10666
+ color: "text-emerald-500"
10667
+ };
10668
+ }
10669
+ function ApiKeysView$1() {
10670
+ const client = (0, _rebasepro_core.useRebaseClient)();
10671
+ const snackbar = (0, _rebasepro_core.useSnackbarController)();
10672
+ const [keys, setKeys] = (0, react.useState)([]);
10673
+ const [loading, setLoading] = (0, react.useState)(true);
10674
+ const [selectedId, setSelectedId] = (0, react.useState)(null);
10675
+ const [showCreate, setShowCreate] = (0, react.useState)(false);
10676
+ const [showSecret, setShowSecret] = (0, react.useState)(null);
10677
+ const [revoking, setRevoking] = (0, react.useState)(null);
10678
+ const clientRef = (0, react.useRef)(client);
10679
+ clientRef.current = client;
10680
+ const snackbarRef = (0, react.useRef)(snackbar);
10681
+ snackbarRef.current = snackbar;
10682
+ const loadKeys = (0, react.useCallback)(async () => {
10683
+ const c = clientRef.current;
10684
+ if (!c?.apiKeys) {
10685
+ setLoading(false);
10686
+ return;
10687
+ }
10688
+ try {
10689
+ setKeys((await c.apiKeys.listKeys()).keys);
10690
+ } catch (e) {
10691
+ snackbarRef.current.open({
10692
+ type: "error",
10693
+ message: e instanceof Error ? e.message : String(e)
10694
+ });
10695
+ } finally {
10696
+ setLoading(false);
10697
+ }
10698
+ }, []);
10699
+ (0, react.useEffect)(() => {
10700
+ loadKeys();
10701
+ }, [loadKeys]);
10702
+ const handleRevoke = async (id) => {
10703
+ const c = clientRef.current;
10704
+ if (!c?.apiKeys) return;
10705
+ setRevoking(id);
10706
+ try {
10707
+ await c.apiKeys.revokeKey(id);
10708
+ snackbarRef.current.open({
10709
+ type: "success",
10710
+ message: "API key revoked"
10711
+ });
10712
+ await loadKeys();
10713
+ if (selectedId === id) setSelectedId(null);
10714
+ } catch (e) {
10715
+ snackbarRef.current.open({
10716
+ type: "error",
10717
+ message: e instanceof Error ? e.message : String(e)
10718
+ });
10719
+ } finally {
10720
+ setRevoking(null);
10721
+ }
10722
+ };
10723
+ const handleCreated = (keyWithSecret) => {
10724
+ setShowCreate(false);
10725
+ setShowSecret(keyWithSecret);
10726
+ loadKeys();
10727
+ };
10728
+ const selectedKey = keys.find((k) => k.id === selectedId);
10729
+ const activeKeys = keys.filter((k) => !k.revoked_at && !isExpired(k));
10730
+ const inactiveKeys = keys.filter((k) => k.revoked_at || isExpired(k));
10731
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10732
+ className: "flex items-center justify-center h-full",
10733
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, {})
10734
+ });
10735
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
10736
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10737
+ className: "flex h-full w-full overflow-hidden bg-white dark:bg-surface-950",
10738
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10739
+ className: (0, _rebasepro_ui.cls)("flex flex-col w-[340px] min-w-[280px] border-r h-full", _rebasepro_ui.defaultBorderMixin),
10740
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10741
+ className: (0, _rebasepro_ui.cls)("flex items-center justify-between px-4 py-2.5 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", _rebasepro_ui.defaultBorderMixin),
10742
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10743
+ className: "flex items-center gap-2",
10744
+ children: [
10745
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
10746
+ size: _rebasepro_ui.iconSize.smallest,
10747
+ className: "text-primary"
10748
+ }),
10749
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10750
+ variant: "subtitle2",
10751
+ className: "font-semibold",
10752
+ children: "API Keys"
10753
+ }),
10754
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
10755
+ size: "smallest",
10756
+ className: "bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300",
10757
+ children: activeKeys.length
10758
+ })
10759
+ ]
10760
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10761
+ className: "flex items-center gap-1",
10762
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
10763
+ size: "small",
10764
+ onClick: loadKeys,
10765
+ title: "Refresh",
10766
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.RefreshCwIcon, { size: _rebasepro_ui.iconSize.smallest })
10767
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
10768
+ size: "small",
10769
+ color: "primary",
10770
+ onClick: () => setShowCreate(true),
10771
+ startIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.PlusIcon, { size: _rebasepro_ui.iconSize.smallest }),
10772
+ children: "New"
10773
+ })]
10774
+ })]
10775
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10776
+ className: "flex-1 overflow-y-auto p-2 space-y-1",
10777
+ children: [
10778
+ activeKeys.length === 0 && inactiveKeys.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10779
+ className: "flex flex-col items-center justify-center h-full gap-3 text-center p-6",
10780
+ children: [
10781
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
10782
+ size: _rebasepro_ui.iconSize.medium,
10783
+ className: "text-surface-300 dark:text-surface-600"
10784
+ }),
10785
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10786
+ variant: "body2",
10787
+ color: "secondary",
10788
+ children: "No API keys yet"
10789
+ }),
10790
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10791
+ variant: "caption",
10792
+ color: "disabled",
10793
+ children: "Create a key to enable machine-to-machine authentication"
10794
+ })
10795
+ ]
10796
+ }),
10797
+ activeKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyListItem, {
10798
+ apiKey: key,
10799
+ selected: selectedId === key.id,
10800
+ onClick: () => setSelectedId(key.id)
10801
+ }, key.id)),
10802
+ inactiveKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10803
+ className: "px-2 pt-3 pb-1",
10804
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10805
+ variant: "caption",
10806
+ color: "disabled",
10807
+ className: "text-[10px] uppercase tracking-wider font-medium",
10808
+ children: "Revoked / Expired"
10809
+ })
10810
+ }), inactiveKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyListItem, {
10811
+ apiKey: key,
10812
+ selected: selectedId === key.id,
10813
+ onClick: () => setSelectedId(key.id)
10814
+ }, key.id))] })
10815
+ ]
10816
+ })]
10817
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10818
+ className: "flex-1 flex flex-col min-w-0 h-full overflow-hidden",
10819
+ children: !selectedKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10820
+ className: "flex items-center justify-center h-full",
10821
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10822
+ variant: "body2",
10823
+ color: "disabled",
10824
+ children: "Select an API key to view details"
10825
+ })
10826
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
10827
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10828
+ className: (0, _rebasepro_ui.cls)("flex items-center justify-between px-5 py-3 border-b bg-white dark:bg-surface-950 min-h-[56px]", _rebasepro_ui.defaultBorderMixin),
10829
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10830
+ className: "flex items-center gap-3 min-w-0",
10831
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
10832
+ size: _rebasepro_ui.iconSize.small,
10833
+ className: "text-primary shrink-0"
10834
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10835
+ className: "min-w-0",
10836
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10837
+ variant: "subtitle1",
10838
+ className: "font-semibold truncate",
10839
+ children: selectedKey.name
10840
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10841
+ variant: "caption",
10842
+ color: "secondary",
10843
+ className: "font-mono text-[11px]",
10844
+ children: [selectedKey.key_prefix, "•••"]
10845
+ })]
10846
+ })]
10847
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10848
+ className: "flex items-center gap-2 shrink-0",
10849
+ children: !selectedKey.revoked_at && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
10850
+ size: "small",
10851
+ color: "error",
10852
+ variant: "outlined",
10853
+ onClick: () => handleRevoke(selectedKey.id),
10854
+ disabled: revoking === selectedKey.id,
10855
+ startIcon: revoking === selectedKey.id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "smallest" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, { size: _rebasepro_ui.iconSize.smallest }),
10856
+ children: "Revoke"
10857
+ })
10858
+ })]
10859
+ }),
10860
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10861
+ className: "px-5 py-4 bg-surface-50 dark:bg-surface-900/50",
10862
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10863
+ className: "grid grid-cols-2 md:grid-cols-4 gap-3",
10864
+ children: [
10865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10866
+ label: "Status",
10867
+ value: keyStatus(selectedKey).label,
10868
+ className: keyStatus(selectedKey).color
10869
+ }),
10870
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10871
+ label: "Created",
10872
+ value: formatRelative(selectedKey.created_at)
10873
+ }),
10874
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10875
+ label: "Last Used",
10876
+ value: formatRelative(selectedKey.last_used_at)
10877
+ }),
10878
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10879
+ label: "Expires",
10880
+ value: selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : "Never"
10881
+ })
10882
+ ]
10883
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10884
+ className: "grid grid-cols-2 gap-3 mt-3",
10885
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10886
+ label: "Rate Limit",
10887
+ value: selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : "Default (1000/15min)"
10888
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10889
+ label: "Created By",
10890
+ value: selectedKey.created_by,
10891
+ mono: true
10892
+ })]
10893
+ })]
10894
+ }),
10895
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10896
+ className: (0, _rebasepro_ui.cls)("flex items-center gap-2 px-5 py-2 border-y bg-white dark:bg-surface-950", _rebasepro_ui.defaultBorderMixin),
10897
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10898
+ variant: "subtitle2",
10899
+ className: "font-semibold text-[13px]",
10900
+ children: "Permissions"
10901
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
10902
+ size: "smallest",
10903
+ className: "bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300",
10904
+ children: selectedKey.permissions.length
10905
+ })]
10906
+ }),
10907
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10908
+ className: "flex-1 overflow-y-auto px-5 py-3",
10909
+ children: selectedKey.permissions.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10910
+ variant: "body2",
10911
+ color: "disabled",
10912
+ children: "No permissions configured"
10913
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10914
+ className: "space-y-2",
10915
+ children: selectedKey.permissions.map((perm, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10916
+ className: (0, _rebasepro_ui.cls)("flex items-center gap-3 px-3 py-2 rounded-lg border", _rebasepro_ui.defaultBorderMixin),
10917
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10918
+ variant: "body2",
10919
+ className: "font-mono text-[13px] font-medium flex-1",
10920
+ children: perm.collection === "*" ? "* (all collections)" : perm.collection
10921
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10922
+ className: "flex items-center gap-1",
10923
+ children: perm.operations.map((op) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
10924
+ size: "smallest",
10925
+ className: (0, _rebasepro_ui.cls)(op === "read" && "bg-emerald-100 dark:bg-emerald-900/30 text-emerald-700 dark:text-emerald-300", op === "write" && "bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-300", op === "delete" && "bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300"),
10926
+ children: op
10927
+ }, op))
10928
+ })]
10929
+ }, idx))
10930
+ })
10931
+ })
10932
+ ] })
10933
+ })]
10934
+ }),
10935
+ showCreate && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreateApiKeyDialog, {
10936
+ onClose: () => setShowCreate(false),
10937
+ onCreated: handleCreated
10938
+ }),
10939
+ showSecret && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SecretDisplayDialog, {
10940
+ keyWithSecret: showSecret,
10941
+ onClose: () => setShowSecret(null)
10942
+ })
10943
+ ] });
10944
+ }
10945
+ function KeyListItem({ apiKey, selected, onClick }) {
10946
+ const status = keyStatus(apiKey);
10947
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10948
+ onClick,
10949
+ className: (0, _rebasepro_ui.cls)("flex items-center gap-3 px-3 py-2.5 rounded-lg cursor-pointer transition-all", selected ? "bg-primary/10 dark:bg-primary/15 ring-1 ring-primary/30" : "hover:bg-surface-100 dark:hover:bg-surface-950"),
10950
+ children: [
10951
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: (0, _rebasepro_ui.cls)("w-2 h-2 rounded-full shrink-0", status.label === "Active" ? "bg-emerald-400" : status.label === "Expired" ? "bg-amber-400" : "bg-red-400") }),
10952
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10953
+ className: "flex-1 min-w-0",
10954
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10955
+ variant: "body2",
10956
+ className: "truncate font-medium text-[13px]",
10957
+ children: apiKey.name
10958
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10959
+ variant: "caption",
10960
+ color: "secondary",
10961
+ className: "truncate text-[11px] font-mono",
10962
+ children: [apiKey.key_prefix, "•••"]
10963
+ })]
10964
+ }),
10965
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10966
+ className: "shrink-0",
10967
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10968
+ variant: "caption",
10969
+ color: "disabled",
10970
+ className: "text-[10px]",
10971
+ children: permissionSummary(apiKey.permissions)
10972
+ })
10973
+ })
10974
+ ]
10975
+ });
10976
+ }
10977
+ function StatCard({ label, value, mono, className }) {
10978
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10979
+ className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
10980
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10981
+ variant: "caption",
10982
+ color: "secondary",
10983
+ className: "text-[10px] uppercase tracking-wider font-medium",
10984
+ children: label
10985
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10986
+ variant: "body2",
10987
+ className: (0, _rebasepro_ui.cls)("mt-0.5 font-semibold text-[13px]", mono && "font-mono", className),
10988
+ children: value
10989
+ })]
10990
+ });
10991
+ }
10992
+ function CreateApiKeyDialog({ onClose, onCreated }) {
10993
+ const client = (0, _rebasepro_core.useRebaseClient)();
10994
+ const snackbar = (0, _rebasepro_core.useSnackbarController)();
10995
+ const [name, setName] = (0, react.useState)("");
10996
+ const [permissions, setPermissions] = (0, react.useState)([{
10997
+ collection: "*",
10998
+ read: true,
10999
+ write: false,
11000
+ delete: false
11001
+ }]);
11002
+ const [rateLimit, setRateLimit] = (0, react.useState)("");
11003
+ const [expiresIn, setExpiresIn] = (0, react.useState)("never");
11004
+ const [creating, setCreating] = (0, react.useState)(false);
11005
+ const addRow = () => setPermissions([...permissions, {
11006
+ collection: "",
11007
+ read: true,
11008
+ write: false,
11009
+ delete: false
11010
+ }]);
11011
+ const removeRow = (idx) => setPermissions(permissions.filter((_, i) => i !== idx));
11012
+ const updateRow = (idx, field, value) => {
11013
+ setPermissions(permissions.map((row, i) => i === idx ? {
11014
+ ...row,
11015
+ [field]: value
11016
+ } : row));
11017
+ };
11018
+ const handleCreate = async () => {
11019
+ if (!client?.apiKeys || !name.trim()) return;
11020
+ const apiPerms = permissions.filter((r) => r.collection.trim()).map((r) => ({
11021
+ collection: r.collection.trim(),
11022
+ operations: [
11023
+ ...r.read ? ["read"] : [],
11024
+ ...r.write ? ["write"] : [],
11025
+ ...r.delete ? ["delete"] : []
11026
+ ]
11027
+ })).filter((p) => p.operations.length > 0);
11028
+ if (apiPerms.length === 0) {
11029
+ snackbar.open({
11030
+ type: "error",
11031
+ message: "At least one permission is required"
11032
+ });
11033
+ return;
11034
+ }
11035
+ let expires_at = null;
11036
+ if (expiresIn === "7d") expires_at = new Date(Date.now() + 7 * 864e5).toISOString();
11037
+ else if (expiresIn === "30d") expires_at = new Date(Date.now() + 30 * 864e5).toISOString();
11038
+ else if (expiresIn === "90d") expires_at = new Date(Date.now() + 90 * 864e5).toISOString();
11039
+ else if (expiresIn === "1y") expires_at = new Date(Date.now() + 365 * 864e5).toISOString();
11040
+ setCreating(true);
11041
+ try {
11042
+ onCreated((await client.apiKeys.createKey({
11043
+ name: name.trim(),
11044
+ permissions: apiPerms,
11045
+ rate_limit: rateLimit ? parseInt(rateLimit, 10) : null,
11046
+ expires_at
11047
+ })).key);
11048
+ } catch (e) {
11049
+ snackbar.open({
11050
+ type: "error",
11051
+ message: e instanceof Error ? e.message : String(e)
11052
+ });
11053
+ } finally {
11054
+ setCreating(false);
11055
+ }
11056
+ };
11057
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
11058
+ open: true,
11059
+ onOpenChange: (open) => {
11060
+ if (!open) onClose();
11061
+ },
11062
+ maxWidth: "lg",
11063
+ children: [
11064
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, { children: "Create API Key" }),
11065
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, {
11066
+ className: "space-y-4",
11067
+ children: [
11068
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
11069
+ label: "Name",
11070
+ value: name,
11071
+ onChange: (e) => setName(e.target.value),
11072
+ placeholder: "e.g. Analytics Pipeline",
11073
+ size: "small",
11074
+ autoFocus: true
11075
+ }),
11076
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
11077
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
11078
+ variant: "caption",
11079
+ color: "secondary",
11080
+ className: "text-[11px] uppercase tracking-wider font-medium mb-2 block",
11081
+ children: "Permissions"
11082
+ }),
11083
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11084
+ className: "space-y-2",
11085
+ children: permissions.map((row, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11086
+ className: (0, _rebasepro_ui.cls)("flex items-center gap-2 p-2 rounded-lg border", _rebasepro_ui.defaultBorderMixin),
11087
+ children: [
11088
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
11089
+ size: "small",
11090
+ value: row.collection,
11091
+ onChange: (e) => updateRow(idx, "collection", e.target.value),
11092
+ placeholder: "Collection slug or *",
11093
+ className: "flex-1"
11094
+ }),
11095
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
11096
+ className: "flex items-center gap-1 text-xs cursor-pointer",
11097
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
11098
+ size: "small",
11099
+ checked: row.read,
11100
+ onCheckedChange: (v) => updateRow(idx, "read", !!v)
11101
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11102
+ className: "text-emerald-600 dark:text-emerald-400",
11103
+ children: "read"
11104
+ })]
11105
+ }),
11106
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
11107
+ className: "flex items-center gap-1 text-xs cursor-pointer",
11108
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
11109
+ size: "small",
11110
+ checked: row.write,
11111
+ onCheckedChange: (v) => updateRow(idx, "write", !!v)
11112
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11113
+ className: "text-blue-600 dark:text-blue-400",
11114
+ children: "write"
11115
+ })]
11116
+ }),
11117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
11118
+ className: "flex items-center gap-1 text-xs cursor-pointer",
11119
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
11120
+ size: "small",
11121
+ checked: row.delete,
11122
+ onCheckedChange: (v) => updateRow(idx, "delete", !!v)
11123
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11124
+ className: "text-red-600 dark:text-red-400",
11125
+ children: "delete"
11126
+ })]
11127
+ }),
11128
+ permissions.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
11129
+ size: "small",
11130
+ onClick: () => removeRow(idx),
11131
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, { size: _rebasepro_ui.iconSize.smallest })
11132
+ })
11133
+ ]
11134
+ }, idx))
11135
+ }),
11136
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11137
+ size: "small",
11138
+ variant: "text",
11139
+ onClick: addRow,
11140
+ className: "mt-1",
11141
+ startIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.PlusIcon, { size: _rebasepro_ui.iconSize.smallest }),
11142
+ children: "Add collection"
11143
+ })
11144
+ ] }),
11145
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11146
+ className: "flex gap-4",
11147
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11148
+ className: "flex-1",
11149
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
11150
+ label: "Expires",
11151
+ value: expiresIn,
11152
+ onValueChange: setExpiresIn,
11153
+ size: "small",
11154
+ renderValue: (v) => v === "never" ? "Never" : v === "7d" ? "7 days" : v === "30d" ? "30 days" : v === "90d" ? "90 days" : "1 year",
11155
+ children: [
11156
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11157
+ value: "never",
11158
+ children: "Never"
11159
+ }),
11160
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11161
+ value: "7d",
11162
+ children: "7 days"
11163
+ }),
11164
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11165
+ value: "30d",
11166
+ children: "30 days"
11167
+ }),
11168
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11169
+ value: "90d",
11170
+ children: "90 days"
11171
+ }),
11172
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11173
+ value: "1y",
11174
+ children: "1 year"
11175
+ })
11176
+ ]
11177
+ })
11178
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11179
+ className: "flex-1",
11180
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
11181
+ label: "Rate Limit (per 15 min)",
11182
+ value: rateLimit,
11183
+ onChange: (e) => setRateLimit(e.target.value.replace(/\D/g, "")),
11184
+ placeholder: "Default: 1000",
11185
+ size: "small"
11186
+ })
11187
+ })]
11188
+ })
11189
+ ]
11190
+ }),
11191
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11192
+ variant: "text",
11193
+ onClick: onClose,
11194
+ children: "Cancel"
11195
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11196
+ color: "primary",
11197
+ onClick: handleCreate,
11198
+ disabled: creating || !name.trim(),
11199
+ startIcon: creating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "smallest" }) : void 0,
11200
+ children: "Create Key"
11201
+ })] })
11202
+ ]
11203
+ });
11204
+ }
11205
+ function SecretDisplayDialog({ keyWithSecret, onClose }) {
11206
+ const snackbar = (0, _rebasepro_core.useSnackbarController)();
11207
+ const [copied, setCopied] = (0, react.useState)(false);
11208
+ const handleCopy = async () => {
11209
+ try {
11210
+ await navigator.clipboard.writeText(keyWithSecret.key);
11211
+ setCopied(true);
11212
+ snackbar.open({
11213
+ type: "success",
11214
+ message: "API key copied to clipboard"
11215
+ });
11216
+ setTimeout(() => setCopied(false), 2e3);
11217
+ } catch {
11218
+ snackbar.open({
11219
+ type: "error",
11220
+ message: "Failed to copy"
11221
+ });
11222
+ }
11223
+ };
11224
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
11225
+ open: true,
11226
+ onOpenChange: (open) => {
11227
+ if (!open) onClose();
11228
+ },
11229
+ maxWidth: "md",
11230
+ children: [
11231
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11232
+ className: "flex items-center gap-2",
11233
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CheckCircleIcon, {
11234
+ size: _rebasepro_ui.iconSize.small,
11235
+ className: "text-emerald-500"
11236
+ }), "API Key Created"]
11237
+ }) }),
11238
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [
11239
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11240
+ className: "p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 mb-4",
11241
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11242
+ className: "flex items-center gap-2 mb-1",
11243
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertCircleIcon, {
11244
+ size: _rebasepro_ui.iconSize.smallest,
11245
+ className: "text-amber-600 dark:text-amber-400"
11246
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
11247
+ variant: "caption",
11248
+ className: "font-semibold text-amber-700 dark:text-amber-400",
11249
+ children: "Copy your key now — it won't be shown again"
11250
+ })]
11251
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
11252
+ variant: "caption",
11253
+ className: "text-amber-600 dark:text-amber-300",
11254
+ children: "This is the only time the full API key will be displayed. Store it securely."
11255
+ })]
11256
+ }),
11257
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11258
+ className: (0, _rebasepro_ui.cls)("flex items-center gap-2 p-3 rounded-lg border bg-surface-50 dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
11259
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
11260
+ className: "flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all",
11261
+ children: keyWithSecret.key
11262
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
11263
+ title: copied ? "Copied!" : "Copy",
11264
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
11265
+ size: "small",
11266
+ onClick: handleCopy,
11267
+ children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CheckCircleIcon, {
11268
+ size: _rebasepro_ui.iconSize.smallest,
11269
+ className: "text-emerald-500"
11270
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CopyIcon, { size: _rebasepro_ui.iconSize.smallest })
11271
+ })
11272
+ })]
11273
+ }),
11274
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11275
+ className: "mt-4 space-y-1",
11276
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
11277
+ variant: "caption",
11278
+ color: "secondary",
11279
+ children: [
11280
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Name:" }),
11281
+ " ",
11282
+ keyWithSecret.name
11283
+ ]
11284
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
11285
+ variant: "caption",
11286
+ color: "secondary",
11287
+ children: [
11288
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Permissions:" }),
11289
+ " ",
11290
+ permissionSummary(keyWithSecret.permissions)
11291
+ ]
11292
+ })]
11293
+ })
11294
+ ] }),
11295
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogActions, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11296
+ color: "primary",
11297
+ onClick: onClose,
11298
+ children: "Done"
11299
+ }) })
11300
+ ]
11301
+ });
11302
+ }
11303
+ var init_ApiKeysView = __esmMin((() => {}));
11304
+ //#endregion
11305
+ //#region src/components/RebaseStudio.tsx
11306
+ var SQLEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_SQLEditor(), SQLEditor_exports)).then((m) => ({ default: m.SQLEditor })));
11307
+ var JSEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_JSEditor(), JSEditor_exports)).then((m) => ({ default: m.JSEditor })));
11308
+ var RLSEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_RLSEditor(), RLSEditor_exports)).then((m) => ({ default: m.RLSEditor })));
11309
+ var StorageView = (0, react.lazy)(() => Promise.resolve().then(() => (init_StorageView(), StorageView_exports)).then((m) => ({ default: m.StorageView })));
11310
+ var CronJobsView = (0, react.lazy)(() => Promise.resolve().then(() => (init_CronJobsView(), CronJobsView_exports)).then((m) => ({ default: m.CronJobsView })));
11311
+ var SchemaVisualizer = (0, react.lazy)(() => Promise.resolve().then(() => (init_SchemaVisualizer(), SchemaVisualizer_exports)).then((m) => ({ default: m.SchemaVisualizer })));
11312
+ var BranchesView = (0, react.lazy)(() => Promise.resolve().then(() => (init_BranchesView(), BranchesView_exports)).then((m) => ({ default: m.BranchesView })));
10419
11313
  var ApiExplorer = (0, react.lazy)(() => Promise.resolve().then(() => (init_ApiExplorer(), ApiExplorer_exports)).then((m) => ({ default: m.ApiExplorer })));
10420
11314
  var LogsExplorer = (0, react.lazy)(() => Promise.resolve().then(() => (init_LogsExplorer(), LogsExplorer_exports)).then((m) => ({ default: m.LogsExplorer })));
11315
+ var ApiKeysView = (0, react.lazy)(() => Promise.resolve().then(() => (init_ApiKeysView(), ApiKeysView_exports)).then((m) => ({ default: m.ApiKeysView })));
10421
11316
  /**
10422
11317
  * Declarative component to configure the Studio in Rebase.
10423
11318
  * Renders nothing — purely registers config into the RebaseRegistry.
@@ -10441,7 +11336,8 @@ return result;
10441
11336
  "schema-visualizer",
10442
11337
  "branches",
10443
11338
  "api",
10444
- "logs"
11339
+ "logs",
11340
+ "api-keys"
10445
11341
  ];
10446
11342
  const suspense = (el) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
10447
11343
  fallback: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgressCenter, {}),
@@ -10519,6 +11415,14 @@ return result;
10519
11415
  description: "Real-time system and query logs",
10520
11416
  view: suspense(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LogsExplorer, {}))
10521
11417
  });
11418
+ if (activeTools.includes("api-keys")) views.push({
11419
+ slug: "api-keys",
11420
+ name: "API Keys",
11421
+ group: "Access Control",
11422
+ icon: "KeyRound",
11423
+ description: "Create and manage scoped API keys",
11424
+ view: suspense(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ApiKeysView, {}))
11425
+ });
10522
11426
  return views;
10523
11427
  }, [tools]);
10524
11428
  const homePageRef = react.default.useRef(resolvedHomePage);