@rebasepro/studio 0.6.0 → 0.7.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 (35) 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-DSucz6wV.js} +4 -4
  6. package/dist/{JSEditor-BhAbEjCP.js.map → JSEditor-DSucz6wV.js.map} +1 -1
  7. package/dist/{LogsExplorer-CqtKILj8.js → LogsExplorer-J4xfsuv3.js} +49 -83
  8. package/dist/LogsExplorer-J4xfsuv3.js.map +1 -0
  9. package/dist/{RLSEditor-CTxYbBdW.js → RLSEditor-BM64laoW.js} +483 -289
  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-nDaC2foF.js → StorageView-CvrnHmDG.js} +89 -76
  16. package/dist/StorageView-CvrnHmDG.js.map +1 -0
  17. package/dist/components/ApiKeys/ApiKeysView.d.ts +2 -0
  18. package/dist/index.es.js +38 -19
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/index.umd.js +1358 -489
  21. package/dist/index.umd.js.map +1 -1
  22. package/package.json +17 -17
  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/LogsExplorer/LogsExplorer.tsx +46 -84
  27. package/src/components/RLSEditor/PolicyEditor.tsx +1 -0
  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 +3 -3
  32. package/src/components/StudioHomePage.tsx +5 -1
  33. package/dist/LogsExplorer-CqtKILj8.js.map +0 -1
  34. package/dist/RLSEditor-CTxYbBdW.js.map +0 -1
  35. package/dist/StorageView-nDaC2foF.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;
@@ -143,17 +143,26 @@
143
143
  label: "Access Control",
144
144
  dotColor: "bg-rose-400",
145
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
- }]
146
+ tools: [
147
+ {
148
+ path: "/users",
149
+ name: "Users",
150
+ description: "Manage developers and assign roles in your workspace",
151
+ icon: "group"
152
+ },
153
+ {
154
+ path: "/roles",
155
+ name: "Roles",
156
+ description: "Create and configure fine-grained access permissions",
157
+ icon: "ShieldEllipsis"
158
+ },
159
+ {
160
+ path: "/api-keys",
161
+ name: "API Keys",
162
+ description: "Create and manage scoped keys for machine-to-machine access",
163
+ icon: "KeyRound"
164
+ }
165
+ ]
157
166
  }
158
167
  ];
159
168
  function StudioHomePage({ additionalActions, additionalChildrenStart, additionalChildrenEnd, sections, hiddenGroups }) {
@@ -896,7 +905,7 @@
896
905
  }) : Object.entries(schemas).map(([schemaName, tables]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
897
906
  className: "mb-2",
898
907
  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",
908
+ className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
900
909
  onClick: () => setExpandedSchemas((prev) => ({
901
910
  ...prev,
902
911
  [schemaName]: !prev[schemaName]
@@ -914,7 +923,7 @@
914
923
  }), expandedSchemas[schemaName] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
915
924
  className: "ml-3 mt-1 space-y-1",
916
925
  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",
926
+ className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative",
918
927
  onClick: () => setExpandedTables((prev) => ({
919
928
  ...prev,
920
929
  [`${schemaName}.${table.tableName}`]: !prev[`${schemaName}.${table.tableName}`]
@@ -1017,7 +1026,7 @@
1017
1026
  }), expandedTables[`${schemaName}.${table.tableName}`] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1018
1027
  className: (0, _rebasepro_ui.cls)("ml-5 mt-1 space-y-0.5 border-l", _rebasepro_ui.defaultBorderMixin),
1019
1028
  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]",
1029
+ 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
1030
  children: [
1022
1031
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1023
1032
  className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0",
@@ -3130,7 +3139,7 @@ declare const context: JSEditorContext;
3130
3139
  function CollectionItem({ collection, onInsertCode }) {
3131
3140
  const [expanded, setExpanded] = (0, react.useState)(false);
3132
3141
  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",
3142
+ className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative",
3134
3143
  onClick: () => setExpanded(!expanded),
3135
3144
  children: [
3136
3145
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
@@ -3172,7 +3181,7 @@ declare const context: JSEditorContext;
3172
3181
  }), expanded && collection.properties.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
3173
3182
  className: (0, _rebasepro_ui.cls)("ml-5 mt-1 space-y-0.5 border-l", _rebasepro_ui.defaultBorderMixin),
3174
3183
  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]",
3184
+ 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
3185
  onClick: () => onInsertCode(`"${prop}"`),
3177
3186
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3178
3187
  className: "w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0",
@@ -3455,7 +3464,7 @@ declare const context: JSEditorContext;
3455
3464
  children: "No execution history yet"
3456
3465
  })
3457
3466
  }) : [...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",
3467
+ 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
3468
  onClick: () => onSelectSnippet(code),
3460
3469
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
3461
3470
  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",
@@ -4819,194 +4828,201 @@ return result;
4819
4828
  open: helpOpen,
4820
4829
  onOpenChange: setHelpOpen,
4821
4830
  maxWidth: "3xl",
4822
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, {
4823
- className: "p-4 sm:p-6 lg:p-8 flex flex-col gap-6",
4824
- children: [
4825
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4826
- variant: "h5",
4827
- className: "mb-2",
4828
- children: t("studio_policy_help_title")
4829
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4830
- className: "text-text-secondary dark:text-text-secondary-dark",
4831
- children: t("studio_policy_help_intro")
4832
- })] }),
4833
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4834
- className: "flex flex-col gap-4",
4835
- children: [
4836
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4837
- className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4838
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4839
- variant: "subtitle2",
4840
- className: "text-primary dark:text-primary-light font-medium",
4841
- children: t("studio_policy_help_step1_title")
4842
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4843
- variant: "body2",
4844
- className: "text-text-secondary dark:text-text-secondary-dark",
4845
- children: t("studio_policy_help_step1_desc")
4846
- })]
4847
- }),
4848
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4849
- className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4850
- children: [
4851
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4852
- variant: "subtitle2",
4853
- className: "text-primary dark:text-primary-light font-medium",
4854
- children: t("studio_policy_help_step2_title")
4855
- }),
4856
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4857
- variant: "body2",
4858
- className: "text-text-secondary dark:text-text-secondary-dark mb-1",
4859
- children: t("studio_policy_help_step2_desc")
4860
- }),
4861
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
4862
- className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
4863
- children: [
4864
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4865
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "public" }),
4866
- ": ",
4867
- t("studio_policy_help_role_public")
4868
- ] }),
4869
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4870
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "authenticated" }),
4871
- ": ",
4872
- t("studio_policy_help_role_authenticated")
4873
- ] }),
4874
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4875
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "anon" }),
4876
- ": ",
4877
- t("studio_policy_help_role_anon")
4878
- ] })
4879
- ]
4880
- })
4881
- ]
4882
- }),
4883
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4884
- className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4885
- children: [
4886
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4887
- variant: "subtitle2",
4888
- className: "text-primary dark:text-primary-light font-medium",
4889
- children: t("studio_policy_help_step3_title")
4890
- }),
4891
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4892
- variant: "body2",
4893
- className: "text-text-secondary dark:text-text-secondary-dark mb-1",
4894
- children: t("studio_policy_help_step3_desc")
4895
- }),
4896
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4897
- className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
4898
- children: "Example: auth.uid() = user_id"
4899
- }),
4900
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4901
- variant: "caption",
4902
- className: "text-text-secondary dark:text-text-secondary-dark",
4903
- children: t("studio_policy_help_step3_example")
4904
- })
4905
- ]
4906
- }),
4907
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4908
- className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4909
- children: [
4910
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4911
- variant: "subtitle2",
4912
- className: "text-primary dark:text-primary-light font-medium",
4913
- children: t("studio_policy_help_step4_title")
4914
- }),
4915
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4916
- variant: "body2",
4917
- className: "text-text-secondary dark:text-text-secondary-dark mb-1",
4918
- children: t("studio_policy_help_step4_desc")
4919
- }),
4920
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4921
- className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
4922
- children: "Example: auth.uid() = user_id"
4923
- }),
4924
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4925
- variant: "caption",
4926
- className: "text-text-secondary dark:text-text-secondary-dark",
4927
- children: t("studio_policy_help_step4_example")
4928
- })
4929
- ]
4930
- }),
4931
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4932
- className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", _rebasepro_ui.defaultBorderMixin),
4933
- children: [
4934
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4831
+ children: [
4832
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
4833
+ hidden: true,
4834
+ children: "Row-Level Security Help"
4835
+ }),
4836
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, {
4837
+ className: "p-4 sm:p-6 lg:p-8 flex flex-col gap-6",
4838
+ children: [
4839
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4840
+ variant: "h5",
4841
+ className: "mb-2",
4842
+ children: t("studio_policy_help_title")
4843
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4844
+ className: "text-text-secondary dark:text-text-secondary-dark",
4845
+ children: t("studio_policy_help_intro")
4846
+ })] }),
4847
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4848
+ className: "flex flex-col gap-4",
4849
+ children: [
4850
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4851
+ className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4852
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4935
4853
  variant: "subtitle2",
4936
4854
  className: "text-primary dark:text-primary-light font-medium",
4937
- children: t("studio_policy_help_auth_vars_title")
4938
- }),
4939
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4855
+ children: t("studio_policy_help_step1_title")
4856
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4940
4857
  variant: "body2",
4941
4858
  className: "text-text-secondary dark:text-text-secondary-dark",
4942
- children: t("studio_policy_help_auth_vars_desc")
4943
- }),
4944
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
4945
- className: "list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal",
4946
- children: [
4947
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4948
- className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
4949
- children: "auth.uid()"
4950
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4951
- className: "block mt-0.5",
4952
- children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4953
- className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
4954
- children: "auth.uid() = user_id"
4955
- })]
4956
- })] }),
4957
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4958
- className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
4959
- children: "auth.jwt()"
4960
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4961
- className: "block mt-0.5",
4962
- children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4963
- className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
4964
- children: "auth.jwt() ->> 'email' = 'admin@example.com'"
4965
- })]
4966
- })] }),
4967
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4968
- className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
4969
- children: "auth.roles()"
4970
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4971
- className: "block mt-0.5",
4972
- children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4973
- className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
4974
- children: "string_to_array(auth.roles(), ',') @> ARRAY['admin']"
4975
- })]
4976
- })] })
4977
- ]
4978
- })
4979
- ]
4980
- })
4981
- ]
4982
- }),
4983
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4984
- className: (0, _rebasepro_ui.cls)("mt-2 flex flex-col sm:flex-row justify-between items-start sm:items-center bg-primary/5 dark:bg-primary-bg-dark/10 p-4 rounded-xl border border-primary/10 dark:border-primary/20", _rebasepro_ui.defaultBorderMixin),
4985
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4986
- variant: "body2",
4987
- className: "text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md",
4988
- children: t("studio_policy_help_docs_cta")
4989
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
4990
- component: "a",
4991
- href: "https://www.postgresql.org/docs/current/sql-createpolicy.html",
4992
- target: "_blank",
4993
- variant: "outlined",
4994
- color: "primary",
4995
- size: "small",
4996
- className: "whitespace-nowrap flex-shrink-0",
4997
- children: t("studio_policy_help_read_docs")
4998
- })]
4859
+ children: t("studio_policy_help_step1_desc")
4860
+ })]
4861
+ }),
4862
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4863
+ className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4864
+ children: [
4865
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4866
+ variant: "subtitle2",
4867
+ className: "text-primary dark:text-primary-light font-medium",
4868
+ children: t("studio_policy_help_step2_title")
4869
+ }),
4870
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4871
+ variant: "body2",
4872
+ className: "text-text-secondary dark:text-text-secondary-dark mb-1",
4873
+ children: t("studio_policy_help_step2_desc")
4874
+ }),
4875
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
4876
+ className: "list-disc pl-5 space-y-1 text-sm text-text-secondary dark:text-text-secondary-dark",
4877
+ children: [
4878
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4879
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "public" }),
4880
+ ": ",
4881
+ t("studio_policy_help_role_public")
4882
+ ] }),
4883
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4884
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "authenticated" }),
4885
+ ": ",
4886
+ t("studio_policy_help_role_authenticated")
4887
+ ] }),
4888
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [
4889
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "anon" }),
4890
+ ": ",
4891
+ t("studio_policy_help_role_anon")
4892
+ ] })
4893
+ ]
4894
+ })
4895
+ ]
4896
+ }),
4897
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4898
+ className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4899
+ children: [
4900
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4901
+ variant: "subtitle2",
4902
+ className: "text-primary dark:text-primary-light font-medium",
4903
+ children: t("studio_policy_help_step3_title")
4904
+ }),
4905
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4906
+ variant: "body2",
4907
+ className: "text-text-secondary dark:text-text-secondary-dark mb-1",
4908
+ children: t("studio_policy_help_step3_desc")
4909
+ }),
4910
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4911
+ className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
4912
+ children: "Example: auth.uid() = user_id"
4913
+ }),
4914
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4915
+ variant: "caption",
4916
+ className: "text-text-secondary dark:text-text-secondary-dark",
4917
+ children: t("studio_policy_help_step3_example")
4918
+ })
4919
+ ]
4920
+ }),
4921
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4922
+ className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-1", _rebasepro_ui.defaultBorderMixin),
4923
+ children: [
4924
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4925
+ variant: "subtitle2",
4926
+ className: "text-primary dark:text-primary-light font-medium",
4927
+ children: t("studio_policy_help_step4_title")
4928
+ }),
4929
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4930
+ variant: "body2",
4931
+ className: "text-text-secondary dark:text-text-secondary-dark mb-1",
4932
+ children: t("studio_policy_help_step4_desc")
4933
+ }),
4934
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
4935
+ className: (0, _rebasepro_ui.cls)("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", _rebasepro_ui.defaultBorderMixin),
4936
+ children: "Example: auth.uid() = user_id"
4937
+ }),
4938
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4939
+ variant: "caption",
4940
+ className: "text-text-secondary dark:text-text-secondary-dark",
4941
+ children: t("studio_policy_help_step4_example")
4942
+ })
4943
+ ]
4944
+ }),
4945
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Paper, {
4946
+ className: (0, _rebasepro_ui.cls)("p-4 sm:p-5 flex flex-col gap-2 bg-primary/5 dark:bg-primary-bg-dark/10", _rebasepro_ui.defaultBorderMixin),
4947
+ children: [
4948
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4949
+ variant: "subtitle2",
4950
+ className: "text-primary dark:text-primary-light font-medium",
4951
+ children: t("studio_policy_help_auth_vars_title")
4952
+ }),
4953
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
4954
+ variant: "body2",
4955
+ className: "text-text-secondary dark:text-text-secondary-dark",
4956
+ children: t("studio_policy_help_auth_vars_desc")
4957
+ }),
4958
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
4959
+ className: "list-disc pl-5 space-y-2 text-sm text-text-secondary dark:text-text-secondary-dark font-normal",
4960
+ children: [
4961
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4962
+ className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
4963
+ children: "auth.uid()"
4964
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4965
+ className: "block mt-0.5",
4966
+ children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4967
+ className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
4968
+ children: "auth.uid() = user_id"
4969
+ })]
4970
+ })] }),
4971
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4972
+ className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
4973
+ children: "auth.jwt()"
4974
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4975
+ className: "block mt-0.5",
4976
+ children: ["Returns the full JWT payload as JSONB so you can check custom claims. Example: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4977
+ className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
4978
+ children: "auth.jwt() ->> 'email' = 'admin@example.com'"
4979
+ })]
4980
+ })] }),
4981
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4982
+ className: "bg-surface-100 dark:bg-surface-950 px-1.5 py-0.5 rounded mr-1 whitespace-nowrap",
4983
+ children: "auth.roles()"
4984
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4985
+ className: "block mt-0.5",
4986
+ children: ["Returns the user's role IDs as a comma-separated string. Best used with: ", /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
4987
+ className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
4988
+ children: "string_to_array(auth.roles(), ',') @> ARRAY['admin']"
4989
+ })]
4990
+ })] })
4991
+ ]
4992
+ })
4993
+ ]
4994
+ })
4995
+ ]
4996
+ }),
4997
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4998
+ className: (0, _rebasepro_ui.cls)("mt-2 flex flex-col sm:flex-row justify-between items-start sm:items-center bg-primary/5 dark:bg-primary-bg-dark/10 p-4 rounded-xl border border-primary/10 dark:border-primary/20", _rebasepro_ui.defaultBorderMixin),
4999
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5000
+ variant: "body2",
5001
+ className: "text-primary dark:text-primary-light mb-4 sm:mb-0 max-w-md",
5002
+ children: t("studio_policy_help_docs_cta")
5003
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5004
+ component: "a",
5005
+ href: "https://www.postgresql.org/docs/current/sql-createpolicy.html",
5006
+ target: "_blank",
5007
+ variant: "outlined",
5008
+ color: "primary",
5009
+ size: "small",
5010
+ className: "whitespace-nowrap flex-shrink-0",
5011
+ children: t("studio_policy_help_read_docs")
5012
+ })]
5013
+ })
5014
+ ]
5015
+ }),
5016
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogActions, {
5017
+ className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
5018
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5019
+ onClick: () => setHelpOpen(false),
5020
+ variant: "filled",
5021
+ color: "primary",
5022
+ children: t("studio_policy_help_got_it")
4999
5023
  })
5000
- ]
5001
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogActions, {
5002
- className: "p-4 sm:px-6 sm:pb-6 pt-0 border-t-0",
5003
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5004
- onClick: () => setHelpOpen(false),
5005
- variant: "filled",
5006
- color: "primary",
5007
- children: t("studio_policy_help_got_it")
5008
5024
  })
5009
- })]
5025
+ ]
5010
5026
  })
5011
5027
  ] });
5012
5028
  };
@@ -5014,6 +5030,12 @@ return result;
5014
5030
  //#endregion
5015
5031
  //#region src/components/RLSEditor/RLSEditor.tsx
5016
5032
  var RLSEditor_exports = /* @__PURE__ */ __exportAll({ RLSEditor: () => RLSEditor$1 });
5033
+ function classifyTableClient(tableName, schemaName, junctionTableNames, isMappedToCollection) {
5034
+ if (_rebasepro_common.REBASE_INTERNAL_SCHEMAS.includes(schemaName) || _rebasepro_common.REBASE_INTERNAL_PREFIXES.some((prefix) => tableName.startsWith(prefix))) return "internal";
5035
+ if (isMappedToCollection) return "collection";
5036
+ if (junctionTableNames.has(tableName)) return "junction";
5037
+ return "other";
5038
+ }
5017
5039
  /**
5018
5040
  * Validates and double-quotes a SQL identifier to prevent injection.
5019
5041
  * Only allows safe Postgres identifiers (letters, digits, underscores).
@@ -5023,6 +5045,81 @@ return result;
5023
5045
  if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) throw new Error(`Invalid SQL identifier: "${name}". Only letters, digits, and underscores are allowed.`);
5024
5046
  return `"${name}"`;
5025
5047
  }
5048
+ function SidebarSection({ title, icon, expanded, onToggle, count, children }) {
5049
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5050
+ className: "mb-2",
5051
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5052
+ className: "flex items-center p-1.5 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors",
5053
+ onClick: onToggle,
5054
+ children: [
5055
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5056
+ 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" : ""),
5057
+ fill: "currentColor",
5058
+ viewBox: "0 0 20 20",
5059
+ 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" })
5060
+ }),
5061
+ icon,
5062
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5063
+ variant: "body2",
5064
+ className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow ml-1.5",
5065
+ children: title
5066
+ }),
5067
+ count !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5068
+ className: "text-[10px] text-text-disabled dark:text-text-disabled-dark font-medium tabular-nums mr-1",
5069
+ children: count
5070
+ })
5071
+ ]
5072
+ }), expanded && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5073
+ className: "ml-3 mt-0.5 space-y-0.5",
5074
+ children
5075
+ })]
5076
+ });
5077
+ }
5078
+ function SidebarTableRow({ table, isSelected, onSelect, badge, dimmed, t }) {
5079
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5080
+ onClick: onSelect,
5081
+ 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"),
5082
+ children: [
5083
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5084
+ className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
5085
+ fill: "none",
5086
+ stroke: "currentColor",
5087
+ viewBox: "0 0 24 24",
5088
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
5089
+ strokeLinecap: "round",
5090
+ strokeLinejoin: "round",
5091
+ strokeWidth: 2,
5092
+ 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"
5093
+ })
5094
+ }),
5095
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5096
+ variant: "body2",
5097
+ className: "text-xs truncate flex-1 min-w-0",
5098
+ children: table.tableName
5099
+ }),
5100
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5101
+ className: "flex items-center gap-1.5 shrink-0 ml-2",
5102
+ children: [
5103
+ badge && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5104
+ 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",
5105
+ children: badge
5106
+ }),
5107
+ table.rlsEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5108
+ title: t("studio_rls_enabled"),
5109
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
5110
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5111
+ title: t("studio_rls_disabled"),
5112
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
5113
+ }),
5114
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5115
+ className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
5116
+ children: table.policies.length
5117
+ })
5118
+ ]
5119
+ })
5120
+ ]
5121
+ });
5122
+ }
5026
5123
  var RLSEditor$1;
5027
5124
  var init_RLSEditor = __esmMin((() => {
5028
5125
  init_PolicyEditor();
@@ -5036,6 +5133,7 @@ return result;
5036
5133
  const [tables, setTables] = (0, react.useState)([]);
5037
5134
  const [selectedTable, setSelectedTable] = (0, react.useState)(null);
5038
5135
  const [activeTab, setActiveTab] = (0, react.useState)(0);
5136
+ const [junctionTableNames, setJunctionTableNames] = (0, react.useState)(/* @__PURE__ */ new Set());
5039
5137
  const [editingPolicy, setEditingPolicy] = (0, react.useState)(null);
5040
5138
  const [sidebarSize, setSidebarSize] = (0, react.useState)(() => {
5041
5139
  try {
@@ -5045,7 +5143,11 @@ return result;
5045
5143
  return 20;
5046
5144
  }
5047
5145
  });
5048
- const [expandedSchemas, setExpandedSchemas] = (0, react.useState)({ public: true });
5146
+ const [expandedSections, setExpandedSections] = (0, react.useState)({
5147
+ collection: true,
5148
+ other: true,
5149
+ internal: false
5150
+ });
5049
5151
  const [sidebarTab, setSidebarTab] = (0, react.useState)("tables");
5050
5152
  (0, react.useEffect)(() => {
5051
5153
  try {
@@ -5127,7 +5229,15 @@ return result;
5127
5229
  });
5128
5230
  const sortedTables = Object.values(tableMap).sort((a, b) => a.tableName.localeCompare(b.tableName));
5129
5231
  setTables(sortedTables);
5130
- if (sortedTables.length > 0 && !selectedTable) setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
5232
+ try {
5233
+ const jRows = extractRows(await databaseAdmin.executeSql(_rebasepro_common.JUNCTION_TABLES_SQL));
5234
+ setJunctionTableNames(new Set(jRows.map((r) => r.table_name).filter(Boolean)));
5235
+ } catch {}
5236
+ if (sortedTables.length > 0 && !selectedTable) {
5237
+ const firstUserTable = sortedTables.find((t) => !_rebasepro_common.REBASE_INTERNAL_SCHEMAS.includes(t.schemaName));
5238
+ if (firstUserTable) setSelectedTable(`${firstUserTable.schemaName}.${firstUserTable.tableName}`);
5239
+ else setSelectedTable(`${sortedTables[0].schemaName}.${sortedTables[0].tableName}`);
5240
+ }
5131
5241
  } catch (e) {
5132
5242
  console.error("RLS fetch error:", e);
5133
5243
  setError("Failed to fetch RLS policies: " + (e instanceof Error ? e.message : String(e)));
@@ -5145,18 +5255,38 @@ return result;
5145
5255
  if (!selectedTable) return null;
5146
5256
  return tables.find((t) => `${t.schemaName}.${t.tableName}` === selectedTable) || null;
5147
5257
  }, [selectedTable, tables]);
5148
- const groupedTables = (0, react.useMemo)(() => {
5149
- const groups = {};
5258
+ /** Categorize tables into 4 buckets for the sidebar. */
5259
+ const categorizedTables = (0, react.useMemo)(() => {
5260
+ const groups = {
5261
+ collection: [],
5262
+ junction: [],
5263
+ internal: [],
5264
+ other: []
5265
+ };
5150
5266
  tables.forEach((table) => {
5151
- if (!groups[table.schemaName]) groups[table.schemaName] = [];
5152
- groups[table.schemaName].push(table);
5267
+ 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);
5268
+ groups[classifyTableClient(table.tableName, table.schemaName, junctionTableNames, isMapped)].push(table);
5153
5269
  });
5154
5270
  return groups;
5155
- }, [tables]);
5271
+ }, [
5272
+ tables,
5273
+ junctionTableNames,
5274
+ collectionRegistry.collections
5275
+ ]);
5156
5276
  const activeCollection = (0, react.useMemo)(() => {
5157
5277
  if (!activeTableData) return null;
5158
5278
  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;
5159
5279
  }, [activeTableData, collectionRegistry.collections]);
5280
+ /** The category of the currently selected table. */
5281
+ const activeTableCategory = (0, react.useMemo)(() => {
5282
+ if (!activeTableData) return null;
5283
+ const isMapped = !!activeCollection;
5284
+ return classifyTableClient(activeTableData.tableName, activeTableData.schemaName, junctionTableNames, isMapped);
5285
+ }, [
5286
+ activeTableData,
5287
+ activeCollection,
5288
+ junctionTableNames
5289
+ ]);
5160
5290
  const mergedPolicies = (0, react.useMemo)(() => {
5161
5291
  if (!activeTableData) return [];
5162
5292
  const policiesMap = {};
@@ -5254,74 +5384,78 @@ return result;
5254
5384
  children: isLoading && tables.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5255
5385
  className: "flex justify-center p-4",
5256
5386
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "small" })
5257
- }) : Object.keys(groupedTables).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5387
+ }) : tables.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5258
5388
  className: "p-4 text-center",
5259
5389
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5260
5390
  variant: "caption",
5261
5391
  className: "text-text-disabled dark:text-text-disabled-dark italic",
5262
5392
  children: t("studio_rls_no_tables")
5263
5393
  })
5264
- }) : Object.entries(groupedTables).map(([schemaName, schemaTables]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5265
- className: "mb-2",
5266
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5267
- className: "flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-950 rounded transition-colors",
5268
- onClick: () => setExpandedSchemas((prev) => ({
5394
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5395
+ categorizedTables.collection.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSection, {
5396
+ title: "Schema Collections",
5397
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DatabaseIcon, {
5398
+ size: 12,
5399
+ className: "text-primary dark:text-primary-light"
5400
+ }),
5401
+ expanded: expandedSections.collection ?? true,
5402
+ onToggle: () => setExpandedSections((prev) => ({
5269
5403
  ...prev,
5270
- [schemaName]: !prev[schemaName]
5404
+ collection: !(prev.collection ?? true)
5271
5405
  })),
5272
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5273
- className: (0, _rebasepro_ui.cls)("w-3 h-3 mr-1 transition-transform", expandedSchemas[schemaName] ? "rotate-90" : ""),
5274
- fill: "currentColor",
5275
- viewBox: "0 0 20 20",
5276
- 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" })
5277
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5278
- variant: "body2",
5279
- className: "text-text-primary dark:text-text-primary-dark font-medium text-xs truncate flex-grow",
5280
- children: schemaName
5281
- })]
5282
- }), expandedSchemas[schemaName] && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5283
- className: "ml-3 mt-1 space-y-0.5",
5284
- children: schemaTables.map((table) => {
5285
- const key = `${table.schemaName}.${table.tableName}`;
5286
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5287
- onClick: () => setSelectedTable(key),
5288
- 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"),
5289
- children: [
5290
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5291
- className: "w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark",
5292
- fill: "none",
5293
- stroke: "currentColor",
5294
- viewBox: "0 0 24 24",
5295
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
5296
- strokeLinecap: "round",
5297
- strokeLinejoin: "round",
5298
- strokeWidth: 2,
5299
- 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"
5300
- })
5301
- }),
5302
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5303
- variant: "body2",
5304
- className: "text-xs truncate flex-1 min-w-0",
5305
- children: table.tableName
5306
- }),
5307
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5308
- className: "flex items-center gap-1.5 shrink-0 ml-2",
5309
- children: [table.rlsEnabled ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5310
- title: t("studio_rls_enabled"),
5311
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-green-500" })
5312
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
5313
- title: t("studio_rls_disabled"),
5314
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "w-1.5 h-1.5 rounded-full bg-orange-400 opacity-50" })
5315
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5316
- className: "text-[10px] opacity-40 group-hover:opacity-100 min-w-[1.2rem] text-right font-medium",
5317
- children: table.policies.length
5318
- })]
5319
- })
5320
- ]
5321
- }, key);
5322
- })
5323
- })]
5324
- }, schemaName))
5406
+ children: categorizedTables.collection.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5407
+ table,
5408
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5409
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5410
+ t
5411
+ }, `${table.schemaName}.${table.tableName}`))
5412
+ }),
5413
+ (categorizedTables.other.length > 0 || categorizedTables.junction.length > 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(SidebarSection, {
5414
+ title: "Other Tables",
5415
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertTriangleIcon, {
5416
+ size: 12,
5417
+ className: "text-yellow-500 dark:text-yellow-400"
5418
+ }),
5419
+ expanded: expandedSections.other ?? true,
5420
+ onToggle: () => setExpandedSections((prev) => ({
5421
+ ...prev,
5422
+ other: !(prev.other ?? true)
5423
+ })),
5424
+ children: [categorizedTables.other.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5425
+ table,
5426
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5427
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5428
+ t
5429
+ }, `${table.schemaName}.${table.tableName}`)), categorizedTables.junction.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5430
+ table,
5431
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5432
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5433
+ badge: "Junction",
5434
+ dimmed: true,
5435
+ t
5436
+ }, `${table.schemaName}.${table.tableName}`))]
5437
+ }),
5438
+ categorizedTables.internal.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarSection, {
5439
+ title: "Rebase Internal",
5440
+ icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.LockIcon, {
5441
+ size: 12,
5442
+ className: "text-text-disabled dark:text-text-disabled-dark"
5443
+ }),
5444
+ expanded: expandedSections.internal ?? false,
5445
+ onToggle: () => setExpandedSections((prev) => ({
5446
+ ...prev,
5447
+ internal: !(prev.internal ?? false)
5448
+ })),
5449
+ count: categorizedTables.internal.length,
5450
+ children: categorizedTables.internal.map((table) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SidebarTableRow, {
5451
+ table,
5452
+ isSelected: selectedTable === `${table.schemaName}.${table.tableName}`,
5453
+ onSelect: () => setSelectedTable(`${table.schemaName}.${table.tableName}`),
5454
+ dimmed: true,
5455
+ t
5456
+ }, `${table.schemaName}.${table.tableName}`))
5457
+ })
5458
+ ] })
5325
5459
  })]
5326
5460
  }), sidebarTab === "info" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5327
5461
  className: "flex flex-col h-full",
@@ -5484,10 +5618,11 @@ return result;
5484
5618
  size: "small",
5485
5619
  onClick: async () => {
5486
5620
  const table = activeTableData.tableName;
5621
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
5487
5622
  const action = activeTableData.rlsEnabled ? "DISABLE" : "ENABLE";
5488
5623
  if (!confirm(`Are you sure you want to ${action.toLowerCase()} Row Level Security on "${table}"?`)) return;
5489
5624
  try {
5490
- await databaseAdmin.executeSql(`ALTER TABLE ${sanitizeSqlIdentifier(table)} ${action} ROW LEVEL SECURITY`);
5625
+ await databaseAdmin.executeSql(`ALTER TABLE ${qualifiedTable} ${action} ROW LEVEL SECURITY`);
5491
5626
  snackbarController.open({
5492
5627
  type: "success",
5493
5628
  message: `RLS ${action.toLowerCase()}d on ${table}`
@@ -5514,7 +5649,6 @@ return result;
5514
5649
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5515
5650
  size: "small",
5516
5651
  color: "primary",
5517
- disabled: !activeCollection,
5518
5652
  onClick: () => setEditingPolicy("new"),
5519
5653
  children: t("studio_rls_create_policy")
5520
5654
  })
@@ -5555,31 +5689,57 @@ return result;
5555
5689
  schema: activeTableData.schemaName,
5556
5690
  table: activeTableData.tableName,
5557
5691
  onSave: async (newPolicy) => {
5558
- if (!activeCollection) return;
5559
- const rule = {
5560
- name: newPolicy.policyname,
5561
- operation: newPolicy.cmd?.toLowerCase(),
5562
- mode: newPolicy.permissive?.toLowerCase(),
5563
- using: newPolicy.qual || void 0,
5564
- withCheck: newPolicy.with_check || void 0,
5565
- roles: newPolicy.roles
5566
- };
5567
- const existingRules = ((0, _rebasepro_types.isPostgresCollection)(activeCollection) ? activeCollection.securityRules : void 0) || [];
5568
- let newRules;
5569
- if (editingPolicy === "new") newRules = [...existingRules, rule];
5570
- else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
5571
- try {
5572
- if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
5573
- method: "POST",
5574
- headers: { "Content-Type": "application/json" },
5575
- body: JSON.stringify({
5576
- collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
5577
- collectionData: { securityRules: newRules }
5578
- })
5579
- })).ok) throw new Error("Failed to save policy");
5580
- snackbarController.open({
5692
+ if (activeCollection) {
5693
+ const rule = {
5694
+ name: newPolicy.policyname,
5695
+ operation: newPolicy.cmd?.toLowerCase(),
5696
+ mode: newPolicy.permissive?.toLowerCase(),
5697
+ using: newPolicy.qual || void 0,
5698
+ withCheck: newPolicy.with_check || void 0,
5699
+ roles: newPolicy.roles
5700
+ };
5701
+ const existingRules = ((0, _rebasepro_types.isPostgresCollection)(activeCollection) ? activeCollection.securityRules : void 0) || [];
5702
+ let newRules;
5703
+ if (editingPolicy === "new") newRules = [...existingRules, rule];
5704
+ else newRules = existingRules.map((r) => r.name === editingPolicy.policyname ? rule : r);
5705
+ try {
5706
+ if (!(await fetch(`${apiUrl}/api/schema-editor/collection/save`, {
5707
+ method: "POST",
5708
+ headers: { "Content-Type": "application/json" },
5709
+ body: JSON.stringify({
5710
+ collectionId: activeCollection.id || activeCollection.path || activeCollection.alias || activeTableData.tableName,
5711
+ collectionData: { securityRules: newRules }
5712
+ })
5713
+ })).ok) throw new Error("Failed to save policy");
5714
+ snackbarController.open({
5715
+ type: "success",
5716
+ message: "Policy saved successfully"
5717
+ });
5718
+ setEditingPolicy(null);
5719
+ fetchRLSData();
5720
+ } catch (e) {
5721
+ snackbarController.open({
5722
+ type: "error",
5723
+ message: e instanceof Error ? e.message : String(e)
5724
+ });
5725
+ }
5726
+ } else try {
5727
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(activeTableData.tableName)}`;
5728
+ const policyName = sanitizeSqlIdentifier(newPolicy.policyname || "unnamed_policy");
5729
+ const cmd = newPolicy.cmd || "ALL";
5730
+ const permissive = (newPolicy.permissive || "PERMISSIVE") === "PERMISSIVE" ? "PERMISSIVE" : "RESTRICTIVE";
5731
+ const roles = newPolicy.roles && newPolicy.roles.length > 0 ? newPolicy.roles.map((r) => sanitizeSqlIdentifier(r)).join(", ") : "public";
5732
+ if (editingPolicy !== "new") await databaseAdmin.executeSql(`DROP POLICY IF EXISTS ${policyName} ON ${qualifiedTable}`);
5733
+ let sql = `CREATE POLICY ${policyName} ON ${qualifiedTable}`;
5734
+ sql += ` AS ${permissive}`;
5735
+ sql += ` FOR ${cmd}`;
5736
+ sql += ` TO ${roles}`;
5737
+ if (newPolicy.qual) sql += ` USING (${newPolicy.qual})`;
5738
+ if (newPolicy.with_check) sql += ` WITH CHECK (${newPolicy.with_check})`;
5739
+ await databaseAdmin.executeSql(sql);
5740
+ snackbarController.open({
5581
5741
  type: "success",
5582
- message: "Policy saved successfully"
5742
+ message: `Policy "${newPolicy.policyname}" applied directly via SQL`
5583
5743
  });
5584
5744
  setEditingPolicy(null);
5585
5745
  fetchRLSData();
@@ -5598,17 +5758,59 @@ return result;
5598
5758
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5599
5759
  className: "max-w-4xl mx-auto flex flex-col gap-6",
5600
5760
  children: [
5601
- activeTableData && !activeCollection && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Alert, {
5761
+ activeTableData && activeTableCategory === "internal" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
5762
+ color: "info",
5763
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5764
+ className: "flex items-start gap-2",
5765
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.LockIcon, {
5766
+ size: 16,
5767
+ className: "shrink-0 mt-0.5"
5768
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5769
+ variant: "body2",
5770
+ className: "mb-1 font-semibold",
5771
+ children: "Rebase System Table"
5772
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5773
+ variant: "caption",
5774
+ className: "opacity-80",
5775
+ children: "This table is managed internally by Rebase. Its security policies are configured automatically. Editing policies on system tables is an advanced operation."
5776
+ })] })]
5777
+ })
5778
+ }),
5779
+ activeTableData && activeTableCategory === "junction" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
5780
+ color: "info",
5781
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5782
+ className: "flex items-start gap-2",
5783
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Link2Icon, {
5784
+ size: 16,
5785
+ className: "shrink-0 mt-0.5"
5786
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5787
+ variant: "body2",
5788
+ className: "mb-1 font-semibold",
5789
+ children: "Junction Table"
5790
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5791
+ variant: "caption",
5792
+ className: "opacity-80",
5793
+ 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."
5794
+ })] })]
5795
+ })
5796
+ }),
5797
+ activeTableData && activeTableCategory === "other" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Alert, {
5602
5798
  color: "warning",
5603
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5604
- variant: "body2",
5605
- className: "mb-1",
5606
- children: "Table not managed by Rebase"
5607
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5608
- variant: "caption",
5609
- className: "opacity-80",
5610
- 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."
5611
- })]
5799
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5800
+ className: "flex items-start gap-2",
5801
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertTriangleIcon, {
5802
+ size: 16,
5803
+ className: "shrink-0 mt-0.5"
5804
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5805
+ variant: "body2",
5806
+ className: "mb-1 font-semibold",
5807
+ children: "Unmapped Table"
5808
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
5809
+ variant: "caption",
5810
+ className: "opacity-80",
5811
+ 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."
5812
+ })] })]
5813
+ })
5612
5814
  }),
5613
5815
  activeTableData && !activeTableData.rlsEnabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5614
5816
  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),
@@ -5722,7 +5924,6 @@ return result;
5722
5924
  variant: "text",
5723
5925
  color: "primary",
5724
5926
  onClick: () => setEditingPolicy(policy),
5725
- disabled: !activeCollection,
5726
5927
  children: t("studio_rls_edit")
5727
5928
  }),
5728
5929
  policy.status !== "code_only" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
@@ -5732,9 +5933,10 @@ return result;
5732
5933
  size: "small",
5733
5934
  onClick: async () => {
5734
5935
  const table = activeTableData.tableName;
5936
+ const qualifiedTable = `${sanitizeSqlIdentifier(activeTableData.schemaName)}.${sanitizeSqlIdentifier(table)}`;
5735
5937
  if (!confirm(`Drop policy "${policy.policyname}" from table "${table}"?`)) return;
5736
5938
  try {
5737
- await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${sanitizeSqlIdentifier(table)}`);
5939
+ await databaseAdmin.executeSql(`DROP POLICY ${sanitizeSqlIdentifier(policy.policyname)} ON ${qualifiedTable}`);
5738
5940
  snackbarController.open({
5739
5941
  type: "success",
5740
5942
  message: `Policy "${policy.policyname}" dropped`
@@ -5771,7 +5973,7 @@ return result;
5771
5973
  className: "text-text-disabled dark:text-text-disabled-dark max-w-sm mb-4",
5772
5974
  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."
5773
5975
  }),
5774
- activeCollection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5976
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
5775
5977
  size: "small",
5776
5978
  variant: "filled",
5777
5979
  color: "primary",
@@ -6180,30 +6382,37 @@ return result;
6180
6382
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
6181
6383
  open: deleteDialogOpen,
6182
6384
  onOpenChange: setDeleteDialogOpen,
6183
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
6184
- variant: "subtitle1",
6185
- className: "mb-2",
6186
- children: "Delete File?"
6187
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
6188
- className: "text-surface-accent-600 dark:text-surface-accent-400",
6189
- children: [
6190
- "Are you sure you want to delete \"",
6191
- file.name,
6192
- "\"? This action cannot be undone."
6193
- ]
6194
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
6195
- variant: "text",
6196
- onClick: () => setDeleteDialogOpen(false),
6197
- children: "Cancel"
6198
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
6199
- variant: "filled",
6200
- color: "error",
6201
- onClick: () => {
6202
- setDeleteDialogOpen(false);
6203
- onDelete();
6204
- },
6205
- children: "Delete"
6206
- })] })]
6385
+ children: [
6386
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
6387
+ hidden: true,
6388
+ children: "Delete File"
6389
+ }),
6390
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
6391
+ variant: "subtitle1",
6392
+ className: "mb-2",
6393
+ children: "Delete File?"
6394
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
6395
+ className: "text-surface-accent-600 dark:text-surface-accent-400",
6396
+ children: [
6397
+ "Are you sure you want to delete \"",
6398
+ file.name,
6399
+ "\"? This action cannot be undone."
6400
+ ]
6401
+ })] }),
6402
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
6403
+ variant: "text",
6404
+ onClick: () => setDeleteDialogOpen(false),
6405
+ children: "Cancel"
6406
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
6407
+ variant: "filled",
6408
+ color: "error",
6409
+ onClick: () => {
6410
+ setDeleteDialogOpen(false);
6411
+ onDelete();
6412
+ },
6413
+ children: "Delete"
6414
+ })] })
6415
+ ]
6207
6416
  })] });
6208
6417
  }
6209
6418
  var StorageView$1;
@@ -7094,31 +7303,38 @@ return result;
7094
7303
  setDeleteDialogTarget(null);
7095
7304
  }
7096
7305
  },
7097
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
7098
- variant: "subtitle1",
7099
- className: "font-semibold mb-2",
7100
- children: deleteDialogTarget === "selection" ? `Delete ${selectedPaths.size} item${selectedPaths.size !== 1 ? "s" : ""}?` : deleteDialogTarget ? `Delete folder "${deleteDialogTarget.name}"?` : "Delete?"
7101
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
7102
- variant: "body2",
7103
- color: "secondary",
7104
- children: deleteDialogTarget === "selection" ? "This will permanently delete all selected files and folders, including their contents. This action cannot be undone." : "This will permanently delete the folder and all of its contents. This action cannot be undone."
7105
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
7106
- variant: "text",
7107
- onClick: () => {
7108
- setDeleteDialogOpen(false);
7109
- setDeleteDialogTarget(null);
7110
- },
7111
- disabled: deleting,
7112
- children: "Cancel"
7113
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.LoadingButton, {
7114
- color: "error",
7115
- loading: deleting,
7116
- onClick: deleteDialogTarget === "selection" ? handleBulkDelete : handleConfirmDeleteFolder,
7117
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, {
7118
- size: 14,
7119
- className: "mr-1"
7120
- }), "Delete"]
7121
- })] })]
7306
+ children: [
7307
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
7308
+ hidden: true,
7309
+ children: "Delete Confirmation"
7310
+ }),
7311
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
7312
+ variant: "subtitle1",
7313
+ className: "font-semibold mb-2",
7314
+ children: deleteDialogTarget === "selection" ? `Delete ${selectedPaths.size} item${selectedPaths.size !== 1 ? "s" : ""}?` : deleteDialogTarget ? `Delete folder "${deleteDialogTarget.name}"?` : "Delete?"
7315
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
7316
+ variant: "body2",
7317
+ color: "secondary",
7318
+ children: deleteDialogTarget === "selection" ? "This will permanently delete all selected files and folders, including their contents. This action cannot be undone." : "This will permanently delete the folder and all of its contents. This action cannot be undone."
7319
+ })] }),
7320
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
7321
+ variant: "text",
7322
+ onClick: () => {
7323
+ setDeleteDialogOpen(false);
7324
+ setDeleteDialogTarget(null);
7325
+ },
7326
+ disabled: deleting,
7327
+ children: "Cancel"
7328
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.LoadingButton, {
7329
+ color: "error",
7330
+ loading: deleting,
7331
+ onClick: deleteDialogTarget === "selection" ? handleBulkDelete : handleConfirmDeleteFolder,
7332
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, {
7333
+ size: 14,
7334
+ className: "mr-1"
7335
+ }), "Delete"]
7336
+ })] })
7337
+ ]
7122
7338
  }),
7123
7339
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
7124
7340
  open: newFolderDialogOpen,
@@ -7128,13 +7344,12 @@ return result;
7128
7344
  setNewFolderName("");
7129
7345
  }
7130
7346
  },
7131
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [
7132
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
7133
- variant: "subtitle1",
7134
- className: "font-semibold mb-4",
7347
+ children: [
7348
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, {
7349
+ hidden: true,
7135
7350
  children: "New Folder"
7136
7351
  }),
7137
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
7352
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
7138
7353
  autoFocus: true,
7139
7354
  size: "small",
7140
7355
  label: "Folder name",
@@ -7148,8 +7363,7 @@ return result;
7148
7363
  },
7149
7364
  disabled: creatingFolder,
7150
7365
  placeholder: "Enter folder name"
7151
- }),
7152
- currentPath && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
7366
+ }), currentPath && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
7153
7367
  variant: "caption",
7154
7368
  color: "secondary",
7155
7369
  className: "mt-2",
@@ -7161,25 +7375,26 @@ return result;
7161
7375
  "/"
7162
7376
  ]
7163
7377
  })]
7164
- })
7165
- ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
7166
- variant: "text",
7167
- onClick: () => {
7168
- setNewFolderDialogOpen(false);
7169
- setNewFolderName("");
7170
- },
7171
- disabled: creatingFolder,
7172
- children: "Cancel"
7173
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.LoadingButton, {
7174
- color: "primary",
7175
- loading: creatingFolder,
7176
- disabled: !newFolderName.trim(),
7177
- onClick: handleCreateFolder,
7178
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.FolderPlusIcon, {
7179
- size: 14,
7180
- className: "mr-1"
7181
- }), "Create"]
7182
- })] })]
7378
+ })] }),
7379
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
7380
+ variant: "text",
7381
+ onClick: () => {
7382
+ setNewFolderDialogOpen(false);
7383
+ setNewFolderName("");
7384
+ },
7385
+ disabled: creatingFolder,
7386
+ children: "Cancel"
7387
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.LoadingButton, {
7388
+ color: "primary",
7389
+ loading: creatingFolder,
7390
+ disabled: !newFolderName.trim(),
7391
+ onClick: handleCreateFolder,
7392
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.FolderPlusIcon, {
7393
+ size: 14,
7394
+ className: "mr-1"
7395
+ }), "Create"]
7396
+ })] })
7397
+ ]
7183
7398
  })
7184
7399
  ]
7185
7400
  });
@@ -7193,7 +7408,7 @@ return result;
7193
7408
  if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
7194
7409
  return `${(ms / 6e4).toFixed(1)}m`;
7195
7410
  }
7196
- function formatRelative$1(iso) {
7411
+ function formatRelative$2(iso) {
7197
7412
  if (!iso) return "—";
7198
7413
  const d = new Date(iso);
7199
7414
  const now = Date.now();
@@ -7481,20 +7696,20 @@ return result;
7481
7696
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7482
7697
  className: "grid grid-cols-2 md:grid-cols-4 gap-3",
7483
7698
  children: [
7484
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7485
7700
  label: "Schedule",
7486
7701
  value: selectedJob.schedule,
7487
7702
  mono: true
7488
7703
  }),
7489
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7704
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7490
7705
  label: "Last Run",
7491
- value: formatRelative$1(selectedJob.lastRunAt)
7706
+ value: formatRelative$2(selectedJob.lastRunAt)
7492
7707
  }),
7493
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7708
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7494
7709
  label: "Next Run",
7495
- value: selectedJob.enabled ? formatRelative$1(selectedJob.nextRunAt) : "Paused"
7710
+ value: selectedJob.enabled ? formatRelative$2(selectedJob.nextRunAt) : "Paused"
7496
7711
  }),
7497
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7712
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7498
7713
  label: "Duration",
7499
7714
  value: selectedJob.lastDurationMs !== void 0 ? formatDuration(selectedJob.lastDurationMs) : "—"
7500
7715
  })
@@ -7503,16 +7718,16 @@ return result;
7503
7718
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7504
7719
  className: "grid grid-cols-3 gap-3 mt-3",
7505
7720
  children: [
7506
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7721
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7507
7722
  label: "Status",
7508
7723
  value: selectedJob.state.toUpperCase(),
7509
7724
  chipColor: selectedJob.state === "error" ? "red" : selectedJob.state === "disabled" ? "gray" : "green"
7510
7725
  }),
7511
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7726
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7512
7727
  label: "Total Runs",
7513
7728
  value: String(selectedJob.totalRuns)
7514
7729
  }),
7515
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
7730
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$2, {
7516
7731
  label: "Failures",
7517
7732
  value: String(selectedJob.totalFailures),
7518
7733
  highlight: selectedJob.totalFailures > 0
@@ -7579,7 +7794,7 @@ return result;
7579
7794
  })]
7580
7795
  });
7581
7796
  }
7582
- function StatCard$1({ label, value, mono, chipColor, highlight }) {
7797
+ function StatCard$2({ label, value, mono, chipColor, highlight }) {
7583
7798
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
7584
7799
  className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
7585
7800
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
@@ -8736,7 +8951,7 @@ return result;
8736
8951
  if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
8737
8952
  return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
8738
8953
  }
8739
- function formatRelative(date) {
8954
+ function formatRelative$1(date) {
8740
8955
  if (!date) return "—";
8741
8956
  const d = date instanceof Date ? date : new Date(date);
8742
8957
  if (isNaN(d.getTime())) return "—";
@@ -8938,7 +9153,7 @@ return result;
8938
9153
  "from ",
8939
9154
  branch.parentDatabase,
8940
9155
  " · ",
8941
- formatRelative(branch.createdAt)
9156
+ formatRelative$1(branch.createdAt)
8942
9157
  ]
8943
9158
  })]
8944
9159
  }),
@@ -9002,21 +9217,21 @@ return result;
9002
9217
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9003
9218
  className: "grid grid-cols-2 md:grid-cols-4 gap-3",
9004
9219
  children: [
9005
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9220
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9006
9221
  label: "Branch Name",
9007
9222
  value: selected.name,
9008
9223
  mono: true
9009
9224
  }),
9010
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9225
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9011
9226
  label: "Source Database",
9012
9227
  value: selected.parentDatabase,
9013
9228
  mono: true
9014
9229
  }),
9015
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9230
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9016
9231
  label: "Created",
9017
- value: formatRelative(selected.createdAt)
9232
+ value: formatRelative$1(selected.createdAt)
9018
9233
  }),
9019
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
9234
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard$1, {
9020
9235
  label: "Size",
9021
9236
  value: formatSize(selected.sizeBytes)
9022
9237
  })
@@ -9118,13 +9333,12 @@ return result;
9118
9333
  }) }),
9119
9334
  branches.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
9120
9335
  label: "Source Database",
9121
- value: sourceBranch ?? "",
9122
- onValueChange: (v) => setSourceBranch(v || void 0),
9336
+ value: sourceBranch ?? "__main__",
9337
+ onValueChange: (v) => setSourceBranch(v === "__main__" ? void 0 : v),
9123
9338
  placeholder: "Default (main database)",
9124
9339
  size: "small",
9125
- renderValue: (v) => v || "Default (main database)",
9126
9340
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
9127
- value: "",
9341
+ value: "__main__",
9128
9342
  children: "Default (main database)"
9129
9343
  }), branches.map((b) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
9130
9344
  value: b.name,
@@ -9165,7 +9379,7 @@ return result;
9165
9379
  ]
9166
9380
  });
9167
9381
  }
9168
- function StatCard({ label, value, mono }) {
9382
+ function StatCard$1({ label, value, mono }) {
9169
9383
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9170
9384
  className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
9171
9385
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
@@ -10154,16 +10368,16 @@ return result;
10154
10368
  var LogsExplorer_exports = /* @__PURE__ */ __exportAll({ LogsExplorer: () => LogsExplorer$1 });
10155
10369
  function LogsExplorer$1() {
10156
10370
  const [logs, setLogs] = (0, react.useState)([]);
10157
- const [level, setLevel] = (0, react.useState)("");
10158
- const [source, setSource] = (0, react.useState)("");
10371
+ const [level, setLevel] = (0, react.useState)("all");
10372
+ const [source, setSource] = (0, react.useState)("all");
10159
10373
  const [search, setSearch] = (0, react.useState)("");
10160
10374
  const [autoScroll, setAutoScroll] = (0, react.useState)(true);
10161
10375
  const containerRef = (0, react.useRef)(null);
10162
10376
  const fetchLogs = (0, react.useCallback)(async () => {
10163
10377
  try {
10164
10378
  const params = new URLSearchParams();
10165
- if (level) params.set("level", level);
10166
- if (source) params.set("source", source);
10379
+ if (level && level !== "all") params.set("level", level);
10380
+ if (source && source !== "all") params.set("source", source);
10167
10381
  if (search) params.set("search", search);
10168
10382
  params.set("limit", "200");
10169
10383
  const resp = await fetch(`/api/logs?${params}`);
@@ -10200,22 +10414,9 @@ return result;
10200
10414
  if (autoScroll && containerRef.current) containerRef.current.scrollTop = containerRef.current.scrollHeight;
10201
10415
  }, [logs, autoScroll]);
10202
10416
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10203
- style: {
10204
- display: "flex",
10205
- flexDirection: "column",
10206
- height: "calc(100vh - 64px)",
10207
- background: "#1e1e2e",
10208
- color: "#cdd6f4"
10209
- },
10417
+ className: "flex flex-col h-[calc(100vh-64px)] w-full bg-surface-50 dark:bg-surface-800",
10210
10418
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10211
- style: {
10212
- display: "flex",
10213
- gap: 8,
10214
- padding: "8px 16px",
10215
- borderBottom: "1px solid #313244",
10216
- alignItems: "center",
10217
- flexWrap: "wrap"
10218
- },
10419
+ className: (0, _rebasepro_ui.cls)("flex gap-2 p-3 border-b items-center flex-wrap shrink-0", _rebasepro_ui.defaultBorderMixin),
10219
10420
  children: [
10220
10421
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
10221
10422
  value: level,
@@ -10224,7 +10425,7 @@ return result;
10224
10425
  placeholder: "All Levels",
10225
10426
  children: [
10226
10427
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
10227
- value: "",
10428
+ value: "all",
10228
10429
  children: "All Levels"
10229
10430
  }),
10230
10431
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
@@ -10252,7 +10453,7 @@ return result;
10252
10453
  placeholder: "All Sources",
10253
10454
  children: [
10254
10455
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
10255
- value: "",
10456
+ value: "all",
10256
10457
  children: "All Sources"
10257
10458
  }),
10258
10459
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
@@ -10285,7 +10486,7 @@ return result;
10285
10486
  className: "flex-1 min-w-[200px]"
10286
10487
  }),
10287
10488
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10288
- className: "flex items-center gap-1.5 cursor-pointer",
10489
+ className: "flex items-center gap-1.5 cursor-pointer ml-2",
10289
10490
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
10290
10491
  id: "auto-scroll",
10291
10492
  checked: autoScroll,
@@ -10294,79 +10495,58 @@ return result;
10294
10495
  padding: false
10295
10496
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Label, {
10296
10497
  htmlFor: "auto-scroll",
10297
- className: "text-xs select-none cursor-pointer",
10498
+ className: "text-xs select-none cursor-pointer text-surface-600 dark:text-surface-400",
10298
10499
  children: "Auto-scroll"
10299
10500
  })]
10300
10501
  }),
10301
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10302
- style: {
10303
- fontSize: 12,
10304
- color: "#6c7086"
10305
- },
10306
- children: [logs.length, " entries"]
10502
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10503
+ className: "ml-auto pl-4",
10504
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10505
+ variant: "caption",
10506
+ color: "secondary",
10507
+ children: [logs.length, " entries"]
10508
+ })
10307
10509
  })
10308
10510
  ]
10309
10511
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10310
10512
  ref: containerRef,
10311
- style: {
10312
- flex: 1,
10313
- overflow: "auto",
10314
- fontFamily: "monospace",
10315
- fontSize: 12,
10316
- padding: "8px 0"
10317
- },
10513
+ className: "flex-1 overflow-auto py-2",
10318
10514
  children: [logs.map((log) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10319
- style: {
10320
- padding: "2px 16px",
10321
- display: "flex",
10322
- gap: 8,
10323
- borderBottom: "1px solid #181825"
10324
- },
10515
+ 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),
10325
10516
  children: [
10326
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10327
- style: {
10328
- color: "#6c7086",
10329
- flexShrink: 0
10330
- },
10517
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10518
+ variant: "body2",
10519
+ color: "secondary",
10520
+ className: "w-[72px] shrink-0 font-mono",
10331
10521
  children: new Date(log.timestamp).toLocaleTimeString()
10332
10522
  }),
10333
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10334
- style: {
10335
- color: LEVEL_COLORS[log.level] || "#cdd6f4",
10336
- width: 40,
10337
- flexShrink: 0,
10338
- textTransform: "uppercase",
10339
- fontWeight: 600
10340
- },
10523
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10524
+ variant: "body2",
10525
+ className: (0, _rebasepro_ui.cls)("w-[48px] shrink-0 uppercase font-semibold font-mono", LEVEL_COLORS[log.level] || "text-surface-500"),
10341
10526
  children: log.level
10342
10527
  }),
10343
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10344
- style: {
10345
- color: SOURCE_COLORS[log.source] || "#cdd6f4",
10346
- width: 64,
10347
- flexShrink: 0
10348
- },
10528
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10529
+ variant: "body2",
10530
+ className: (0, _rebasepro_ui.cls)("w-[80px] shrink-0 font-mono", SOURCE_COLORS[log.source] || "text-surface-500"),
10349
10531
  children: [
10350
10532
  "[",
10351
10533
  log.source,
10352
10534
  "]"
10353
10535
  ]
10354
10536
  }),
10355
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10356
- style: {
10357
- color: "#cdd6f4",
10358
- flex: 1
10359
- },
10537
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10538
+ variant: "body2",
10539
+ className: "flex-1 font-mono break-all whitespace-pre-wrap text-surface-900 dark:text-surface-100",
10360
10540
  children: log.message
10361
10541
  })
10362
10542
  ]
10363
10543
  }, log.id)), logs.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10364
- style: {
10365
- padding: 32,
10366
- textAlign: "center",
10367
- color: "#6c7086"
10368
- },
10369
- children: "No log entries yet. Logs will appear here as requests come in."
10544
+ className: "p-8 text-center",
10545
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10546
+ variant: "body2",
10547
+ color: "secondary",
10548
+ children: "No log entries yet. Logs will appear here as requests come in."
10549
+ })
10370
10550
  })]
10371
10551
  })]
10372
10552
  });
@@ -10374,20 +10554,699 @@ return result;
10374
10554
  var LEVEL_COLORS, SOURCE_COLORS;
10375
10555
  var init_LogsExplorer = __esmMin((() => {
10376
10556
  LEVEL_COLORS = {
10377
- debug: "#6c7086",
10378
- info: "#89b4fa",
10379
- warn: "#f9e2af",
10380
- error: "#f38ba8"
10557
+ debug: "text-surface-500",
10558
+ info: "text-blue-600 dark:text-blue-500",
10559
+ warn: "text-amber-600 dark:text-amber-500",
10560
+ error: "text-red-600 dark:text-red-500"
10381
10561
  };
10382
10562
  SOURCE_COLORS = {
10383
- api: "#74c7ec",
10384
- auth: "#cba6f7",
10385
- storage: "#a6e3a1",
10386
- realtime: "#fab387",
10387
- system: "#6c7086"
10563
+ api: "text-sky-600 dark:text-sky-400",
10564
+ auth: "text-purple-600 dark:text-purple-400",
10565
+ storage: "text-green-600 dark:text-green-500",
10566
+ realtime: "text-orange-600 dark:text-orange-400",
10567
+ system: "text-surface-600 dark:text-surface-400"
10388
10568
  };
10389
10569
  }));
10390
10570
  //#endregion
10571
+ //#region src/components/ApiKeys/ApiKeysView.tsx
10572
+ var ApiKeysView_exports = /* @__PURE__ */ __exportAll({ ApiKeysView: () => ApiKeysView$1 });
10573
+ function formatRelative(iso) {
10574
+ if (!iso) return "—";
10575
+ const d = new Date(iso);
10576
+ const now = Date.now();
10577
+ const diff = d.getTime() - now;
10578
+ const abs = Math.abs(diff);
10579
+ if (abs < 6e4) return diff > 0 ? "in <1m" : "<1m ago";
10580
+ if (abs < 36e5) {
10581
+ const m = Math.round(abs / 6e4);
10582
+ return diff > 0 ? `in ${m}m` : `${m}m ago`;
10583
+ }
10584
+ if (abs < 864e5) {
10585
+ const h = Math.round(abs / 36e5);
10586
+ return diff > 0 ? `in ${h}h` : `${h}h ago`;
10587
+ }
10588
+ return d.toLocaleDateString();
10589
+ }
10590
+ function permissionSummary(perms) {
10591
+ if (perms.length === 0) return "No permissions";
10592
+ const wildcard = perms.find((p) => p.collection === "*");
10593
+ if (wildcard) return `All collections (${wildcard.operations.join(", ")})`;
10594
+ if (perms.length === 1) return `${perms[0].collection} (${perms[0].operations.join(", ")})`;
10595
+ return `${perms.length} collections`;
10596
+ }
10597
+ function isExpired(key) {
10598
+ return !!(key.expires_at && new Date(key.expires_at) < /* @__PURE__ */ new Date());
10599
+ }
10600
+ function keyStatus(key) {
10601
+ if (key.revoked_at) return {
10602
+ label: "Revoked",
10603
+ color: "text-red-500"
10604
+ };
10605
+ if (isExpired(key)) return {
10606
+ label: "Expired",
10607
+ color: "text-amber-500"
10608
+ };
10609
+ return {
10610
+ label: "Active",
10611
+ color: "text-emerald-500"
10612
+ };
10613
+ }
10614
+ function ApiKeysView$1() {
10615
+ const client = (0, _rebasepro_core.useRebaseClient)();
10616
+ const snackbar = (0, _rebasepro_core.useSnackbarController)();
10617
+ const [keys, setKeys] = (0, react.useState)([]);
10618
+ const [loading, setLoading] = (0, react.useState)(true);
10619
+ const [selectedId, setSelectedId] = (0, react.useState)(null);
10620
+ const [showCreate, setShowCreate] = (0, react.useState)(false);
10621
+ const [showSecret, setShowSecret] = (0, react.useState)(null);
10622
+ const [revoking, setRevoking] = (0, react.useState)(null);
10623
+ const clientRef = (0, react.useRef)(client);
10624
+ clientRef.current = client;
10625
+ const snackbarRef = (0, react.useRef)(snackbar);
10626
+ snackbarRef.current = snackbar;
10627
+ const loadKeys = (0, react.useCallback)(async () => {
10628
+ const c = clientRef.current;
10629
+ if (!c?.apiKeys) {
10630
+ setLoading(false);
10631
+ return;
10632
+ }
10633
+ try {
10634
+ setKeys((await c.apiKeys.listKeys()).keys);
10635
+ } catch (e) {
10636
+ snackbarRef.current.open({
10637
+ type: "error",
10638
+ message: e instanceof Error ? e.message : String(e)
10639
+ });
10640
+ } finally {
10641
+ setLoading(false);
10642
+ }
10643
+ }, []);
10644
+ (0, react.useEffect)(() => {
10645
+ loadKeys();
10646
+ }, [loadKeys]);
10647
+ const handleRevoke = async (id) => {
10648
+ const c = clientRef.current;
10649
+ if (!c?.apiKeys) return;
10650
+ setRevoking(id);
10651
+ try {
10652
+ await c.apiKeys.revokeKey(id);
10653
+ snackbarRef.current.open({
10654
+ type: "success",
10655
+ message: "API key revoked"
10656
+ });
10657
+ await loadKeys();
10658
+ if (selectedId === id) setSelectedId(null);
10659
+ } catch (e) {
10660
+ snackbarRef.current.open({
10661
+ type: "error",
10662
+ message: e instanceof Error ? e.message : String(e)
10663
+ });
10664
+ } finally {
10665
+ setRevoking(null);
10666
+ }
10667
+ };
10668
+ const handleCreated = (keyWithSecret) => {
10669
+ setShowCreate(false);
10670
+ setShowSecret(keyWithSecret);
10671
+ loadKeys();
10672
+ };
10673
+ const selectedKey = keys.find((k) => k.id === selectedId);
10674
+ const activeKeys = keys.filter((k) => !k.revoked_at && !isExpired(k));
10675
+ const inactiveKeys = keys.filter((k) => k.revoked_at || isExpired(k));
10676
+ if (loading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10677
+ className: "flex items-center justify-center h-full",
10678
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, {})
10679
+ });
10680
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
10681
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10682
+ className: "flex h-full w-full overflow-hidden bg-white dark:bg-surface-950",
10683
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10684
+ className: (0, _rebasepro_ui.cls)("flex flex-col w-[340px] min-w-[280px] border-r h-full", _rebasepro_ui.defaultBorderMixin),
10685
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10686
+ 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),
10687
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10688
+ className: "flex items-center gap-2",
10689
+ children: [
10690
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
10691
+ size: _rebasepro_ui.iconSize.smallest,
10692
+ className: "text-primary"
10693
+ }),
10694
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10695
+ variant: "subtitle2",
10696
+ className: "font-semibold",
10697
+ children: "API Keys"
10698
+ }),
10699
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
10700
+ size: "smallest",
10701
+ className: "bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300",
10702
+ children: activeKeys.length
10703
+ })
10704
+ ]
10705
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10706
+ className: "flex items-center gap-1",
10707
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
10708
+ size: "small",
10709
+ onClick: loadKeys,
10710
+ title: "Refresh",
10711
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.RefreshCwIcon, { size: _rebasepro_ui.iconSize.smallest })
10712
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
10713
+ size: "small",
10714
+ color: "primary",
10715
+ onClick: () => setShowCreate(true),
10716
+ startIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.PlusIcon, { size: _rebasepro_ui.iconSize.smallest }),
10717
+ children: "New"
10718
+ })]
10719
+ })]
10720
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10721
+ className: "flex-1 overflow-y-auto p-2 space-y-1",
10722
+ children: [
10723
+ activeKeys.length === 0 && inactiveKeys.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10724
+ className: "flex flex-col items-center justify-center h-full gap-3 text-center p-6",
10725
+ children: [
10726
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
10727
+ size: _rebasepro_ui.iconSize.medium,
10728
+ className: "text-surface-300 dark:text-surface-600"
10729
+ }),
10730
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10731
+ variant: "body2",
10732
+ color: "secondary",
10733
+ children: "No API keys yet"
10734
+ }),
10735
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10736
+ variant: "caption",
10737
+ color: "disabled",
10738
+ children: "Create a key to enable machine-to-machine authentication"
10739
+ })
10740
+ ]
10741
+ }),
10742
+ activeKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyListItem, {
10743
+ apiKey: key,
10744
+ selected: selectedId === key.id,
10745
+ onClick: () => setSelectedId(key.id)
10746
+ }, key.id)),
10747
+ inactiveKeys.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10748
+ className: "px-2 pt-3 pb-1",
10749
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10750
+ variant: "caption",
10751
+ color: "disabled",
10752
+ className: "text-[10px] uppercase tracking-wider font-medium",
10753
+ children: "Revoked / Expired"
10754
+ })
10755
+ }), inactiveKeys.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(KeyListItem, {
10756
+ apiKey: key,
10757
+ selected: selectedId === key.id,
10758
+ onClick: () => setSelectedId(key.id)
10759
+ }, key.id))] })
10760
+ ]
10761
+ })]
10762
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10763
+ className: "flex-1 flex flex-col min-w-0 h-full overflow-hidden",
10764
+ children: !selectedKey ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10765
+ className: "flex items-center justify-center h-full",
10766
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10767
+ variant: "body2",
10768
+ color: "disabled",
10769
+ children: "Select an API key to view details"
10770
+ })
10771
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
10772
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10773
+ 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),
10774
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10775
+ className: "flex items-center gap-3 min-w-0",
10776
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.KeyRoundIcon, {
10777
+ size: _rebasepro_ui.iconSize.small,
10778
+ className: "text-primary shrink-0"
10779
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10780
+ className: "min-w-0",
10781
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10782
+ variant: "subtitle1",
10783
+ className: "font-semibold truncate",
10784
+ children: selectedKey.name
10785
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10786
+ variant: "caption",
10787
+ color: "secondary",
10788
+ className: "font-mono text-[11px]",
10789
+ children: [selectedKey.key_prefix, "•••"]
10790
+ })]
10791
+ })]
10792
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10793
+ className: "flex items-center gap-2 shrink-0",
10794
+ children: !selectedKey.revoked_at && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
10795
+ size: "small",
10796
+ color: "error",
10797
+ variant: "outlined",
10798
+ onClick: () => handleRevoke(selectedKey.id),
10799
+ disabled: revoking === selectedKey.id,
10800
+ 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 }),
10801
+ children: "Revoke"
10802
+ })
10803
+ })]
10804
+ }),
10805
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10806
+ className: "px-5 py-4 bg-surface-50 dark:bg-surface-900/50",
10807
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10808
+ className: "grid grid-cols-2 md:grid-cols-4 gap-3",
10809
+ children: [
10810
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10811
+ label: "Status",
10812
+ value: keyStatus(selectedKey).label,
10813
+ className: keyStatus(selectedKey).color
10814
+ }),
10815
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10816
+ label: "Created",
10817
+ value: formatRelative(selectedKey.created_at)
10818
+ }),
10819
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10820
+ label: "Last Used",
10821
+ value: formatRelative(selectedKey.last_used_at)
10822
+ }),
10823
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10824
+ label: "Expires",
10825
+ value: selectedKey.expires_at ? formatRelative(selectedKey.expires_at) : "Never"
10826
+ })
10827
+ ]
10828
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10829
+ className: "grid grid-cols-2 gap-3 mt-3",
10830
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10831
+ label: "Rate Limit",
10832
+ value: selectedKey.rate_limit ? `${selectedKey.rate_limit}/15min` : "Default (1000/15min)"
10833
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatCard, {
10834
+ label: "Created By",
10835
+ value: selectedKey.created_by,
10836
+ mono: true
10837
+ })]
10838
+ })]
10839
+ }),
10840
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10841
+ 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),
10842
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10843
+ variant: "subtitle2",
10844
+ className: "font-semibold text-[13px]",
10845
+ children: "Permissions"
10846
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
10847
+ size: "smallest",
10848
+ className: "bg-surface-200 dark:bg-surface-700 text-surface-600 dark:text-surface-300",
10849
+ children: selectedKey.permissions.length
10850
+ })]
10851
+ }),
10852
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10853
+ className: "flex-1 overflow-y-auto px-5 py-3",
10854
+ children: selectedKey.permissions.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10855
+ variant: "body2",
10856
+ color: "disabled",
10857
+ children: "No permissions configured"
10858
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10859
+ className: "space-y-2",
10860
+ children: selectedKey.permissions.map((perm, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10861
+ className: (0, _rebasepro_ui.cls)("flex items-center gap-3 px-3 py-2 rounded-lg border", _rebasepro_ui.defaultBorderMixin),
10862
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10863
+ variant: "body2",
10864
+ className: "font-mono text-[13px] font-medium flex-1",
10865
+ children: perm.collection === "*" ? "* (all collections)" : perm.collection
10866
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10867
+ className: "flex items-center gap-1",
10868
+ children: perm.operations.map((op) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Chip, {
10869
+ size: "smallest",
10870
+ 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"),
10871
+ children: op
10872
+ }, op))
10873
+ })]
10874
+ }, idx))
10875
+ })
10876
+ })
10877
+ ] })
10878
+ })]
10879
+ }),
10880
+ showCreate && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CreateApiKeyDialog, {
10881
+ onClose: () => setShowCreate(false),
10882
+ onCreated: handleCreated
10883
+ }),
10884
+ showSecret && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SecretDisplayDialog, {
10885
+ keyWithSecret: showSecret,
10886
+ onClose: () => setShowSecret(null)
10887
+ })
10888
+ ] });
10889
+ }
10890
+ function KeyListItem({ apiKey, selected, onClick }) {
10891
+ const status = keyStatus(apiKey);
10892
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10893
+ onClick,
10894
+ 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"),
10895
+ children: [
10896
+ /* @__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") }),
10897
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10898
+ className: "flex-1 min-w-0",
10899
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10900
+ variant: "body2",
10901
+ className: "truncate font-medium text-[13px]",
10902
+ children: apiKey.name
10903
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
10904
+ variant: "caption",
10905
+ color: "secondary",
10906
+ className: "truncate text-[11px] font-mono",
10907
+ children: [apiKey.key_prefix, "•••"]
10908
+ })]
10909
+ }),
10910
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10911
+ className: "shrink-0",
10912
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10913
+ variant: "caption",
10914
+ color: "disabled",
10915
+ className: "text-[10px]",
10916
+ children: permissionSummary(apiKey.permissions)
10917
+ })
10918
+ })
10919
+ ]
10920
+ });
10921
+ }
10922
+ function StatCard({ label, value, mono, className }) {
10923
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10924
+ className: (0, _rebasepro_ui.cls)("px-3 py-2 rounded-lg border bg-white dark:bg-surface-900", _rebasepro_ui.defaultBorderMixin),
10925
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10926
+ variant: "caption",
10927
+ color: "secondary",
10928
+ className: "text-[10px] uppercase tracking-wider font-medium",
10929
+ children: label
10930
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
10931
+ variant: "body2",
10932
+ className: (0, _rebasepro_ui.cls)("mt-0.5 font-semibold text-[13px]", mono && "font-mono", className),
10933
+ children: value
10934
+ })]
10935
+ });
10936
+ }
10937
+ function CreateApiKeyDialog({ onClose, onCreated }) {
10938
+ const client = (0, _rebasepro_core.useRebaseClient)();
10939
+ const snackbar = (0, _rebasepro_core.useSnackbarController)();
10940
+ const [name, setName] = (0, react.useState)("");
10941
+ const [permissions, setPermissions] = (0, react.useState)([{
10942
+ collection: "*",
10943
+ read: true,
10944
+ write: false,
10945
+ delete: false
10946
+ }]);
10947
+ const [rateLimit, setRateLimit] = (0, react.useState)("");
10948
+ const [expiresIn, setExpiresIn] = (0, react.useState)("never");
10949
+ const [creating, setCreating] = (0, react.useState)(false);
10950
+ const addRow = () => setPermissions([...permissions, {
10951
+ collection: "",
10952
+ read: true,
10953
+ write: false,
10954
+ delete: false
10955
+ }]);
10956
+ const removeRow = (idx) => setPermissions(permissions.filter((_, i) => i !== idx));
10957
+ const updateRow = (idx, field, value) => {
10958
+ setPermissions(permissions.map((row, i) => i === idx ? {
10959
+ ...row,
10960
+ [field]: value
10961
+ } : row));
10962
+ };
10963
+ const handleCreate = async () => {
10964
+ if (!client?.apiKeys || !name.trim()) return;
10965
+ const apiPerms = permissions.filter((r) => r.collection.trim()).map((r) => ({
10966
+ collection: r.collection.trim(),
10967
+ operations: [
10968
+ ...r.read ? ["read"] : [],
10969
+ ...r.write ? ["write"] : [],
10970
+ ...r.delete ? ["delete"] : []
10971
+ ]
10972
+ })).filter((p) => p.operations.length > 0);
10973
+ if (apiPerms.length === 0) {
10974
+ snackbar.open({
10975
+ type: "error",
10976
+ message: "At least one permission is required"
10977
+ });
10978
+ return;
10979
+ }
10980
+ let expires_at = null;
10981
+ if (expiresIn === "7d") expires_at = new Date(Date.now() + 7 * 864e5).toISOString();
10982
+ else if (expiresIn === "30d") expires_at = new Date(Date.now() + 30 * 864e5).toISOString();
10983
+ else if (expiresIn === "90d") expires_at = new Date(Date.now() + 90 * 864e5).toISOString();
10984
+ else if (expiresIn === "1y") expires_at = new Date(Date.now() + 365 * 864e5).toISOString();
10985
+ setCreating(true);
10986
+ try {
10987
+ onCreated((await client.apiKeys.createKey({
10988
+ name: name.trim(),
10989
+ permissions: apiPerms,
10990
+ rate_limit: rateLimit ? parseInt(rateLimit, 10) : null,
10991
+ expires_at
10992
+ })).key);
10993
+ } catch (e) {
10994
+ snackbar.open({
10995
+ type: "error",
10996
+ message: e instanceof Error ? e.message : String(e)
10997
+ });
10998
+ } finally {
10999
+ setCreating(false);
11000
+ }
11001
+ };
11002
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
11003
+ open: true,
11004
+ onOpenChange: (open) => {
11005
+ if (!open) onClose();
11006
+ },
11007
+ maxWidth: "lg",
11008
+ children: [
11009
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, { children: "Create API Key" }),
11010
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, {
11011
+ className: "space-y-4",
11012
+ children: [
11013
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
11014
+ label: "Name",
11015
+ value: name,
11016
+ onChange: (e) => setName(e.target.value),
11017
+ placeholder: "e.g. Analytics Pipeline",
11018
+ size: "small",
11019
+ autoFocus: true
11020
+ }),
11021
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
11022
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
11023
+ variant: "caption",
11024
+ color: "secondary",
11025
+ className: "text-[11px] uppercase tracking-wider font-medium mb-2 block",
11026
+ children: "Permissions"
11027
+ }),
11028
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11029
+ className: "space-y-2",
11030
+ children: permissions.map((row, idx) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11031
+ className: (0, _rebasepro_ui.cls)("flex items-center gap-2 p-2 rounded-lg border", _rebasepro_ui.defaultBorderMixin),
11032
+ children: [
11033
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
11034
+ size: "small",
11035
+ value: row.collection,
11036
+ onChange: (e) => updateRow(idx, "collection", e.target.value),
11037
+ placeholder: "Collection slug or *",
11038
+ className: "flex-1"
11039
+ }),
11040
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
11041
+ className: "flex items-center gap-1 text-xs cursor-pointer",
11042
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
11043
+ size: "small",
11044
+ checked: row.read,
11045
+ onCheckedChange: (v) => updateRow(idx, "read", !!v)
11046
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11047
+ className: "text-emerald-600 dark:text-emerald-400",
11048
+ children: "read"
11049
+ })]
11050
+ }),
11051
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
11052
+ className: "flex items-center gap-1 text-xs cursor-pointer",
11053
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
11054
+ size: "small",
11055
+ checked: row.write,
11056
+ onCheckedChange: (v) => updateRow(idx, "write", !!v)
11057
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11058
+ className: "text-blue-600 dark:text-blue-400",
11059
+ children: "write"
11060
+ })]
11061
+ }),
11062
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
11063
+ className: "flex items-center gap-1 text-xs cursor-pointer",
11064
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Checkbox, {
11065
+ size: "small",
11066
+ checked: row.delete,
11067
+ onCheckedChange: (v) => updateRow(idx, "delete", !!v)
11068
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11069
+ className: "text-red-600 dark:text-red-400",
11070
+ children: "delete"
11071
+ })]
11072
+ }),
11073
+ permissions.length > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
11074
+ size: "small",
11075
+ onClick: () => removeRow(idx),
11076
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Trash2Icon, { size: _rebasepro_ui.iconSize.smallest })
11077
+ })
11078
+ ]
11079
+ }, idx))
11080
+ }),
11081
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11082
+ size: "small",
11083
+ variant: "text",
11084
+ onClick: addRow,
11085
+ className: "mt-1",
11086
+ startIcon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.PlusIcon, { size: _rebasepro_ui.iconSize.smallest }),
11087
+ children: "Add collection"
11088
+ })
11089
+ ] }),
11090
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11091
+ className: "flex gap-4",
11092
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11093
+ className: "flex-1",
11094
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Select, {
11095
+ label: "Expires",
11096
+ value: expiresIn,
11097
+ onValueChange: setExpiresIn,
11098
+ size: "small",
11099
+ renderValue: (v) => v === "never" ? "Never" : v === "7d" ? "7 days" : v === "30d" ? "30 days" : v === "90d" ? "90 days" : "1 year",
11100
+ children: [
11101
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11102
+ value: "never",
11103
+ children: "Never"
11104
+ }),
11105
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11106
+ value: "7d",
11107
+ children: "7 days"
11108
+ }),
11109
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11110
+ value: "30d",
11111
+ children: "30 days"
11112
+ }),
11113
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11114
+ value: "90d",
11115
+ children: "90 days"
11116
+ }),
11117
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.SelectItem, {
11118
+ value: "1y",
11119
+ children: "1 year"
11120
+ })
11121
+ ]
11122
+ })
11123
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
11124
+ className: "flex-1",
11125
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.TextField, {
11126
+ label: "Rate Limit (per 15 min)",
11127
+ value: rateLimit,
11128
+ onChange: (e) => setRateLimit(e.target.value.replace(/\D/g, "")),
11129
+ placeholder: "Default: 1000",
11130
+ size: "small"
11131
+ })
11132
+ })]
11133
+ })
11134
+ ]
11135
+ }),
11136
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogActions, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11137
+ variant: "text",
11138
+ onClick: onClose,
11139
+ children: "Cancel"
11140
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11141
+ color: "primary",
11142
+ onClick: handleCreate,
11143
+ disabled: creating || !name.trim(),
11144
+ startIcon: creating ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgress, { size: "smallest" }) : void 0,
11145
+ children: "Create Key"
11146
+ })] })
11147
+ ]
11148
+ });
11149
+ }
11150
+ function SecretDisplayDialog({ keyWithSecret, onClose }) {
11151
+ const snackbar = (0, _rebasepro_core.useSnackbarController)();
11152
+ const [copied, setCopied] = (0, react.useState)(false);
11153
+ const handleCopy = async () => {
11154
+ try {
11155
+ await navigator.clipboard.writeText(keyWithSecret.key);
11156
+ setCopied(true);
11157
+ snackbar.open({
11158
+ type: "success",
11159
+ message: "API key copied to clipboard"
11160
+ });
11161
+ setTimeout(() => setCopied(false), 2e3);
11162
+ } catch {
11163
+ snackbar.open({
11164
+ type: "error",
11165
+ message: "Failed to copy"
11166
+ });
11167
+ }
11168
+ };
11169
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Dialog, {
11170
+ open: true,
11171
+ onOpenChange: (open) => {
11172
+ if (!open) onClose();
11173
+ },
11174
+ maxWidth: "md",
11175
+ children: [
11176
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogTitle, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11177
+ className: "flex items-center gap-2",
11178
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CheckCircleIcon, {
11179
+ size: _rebasepro_ui.iconSize.small,
11180
+ className: "text-emerald-500"
11181
+ }), "API Key Created"]
11182
+ }) }),
11183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.DialogContent, { children: [
11184
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11185
+ className: "p-3 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 mb-4",
11186
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11187
+ className: "flex items-center gap-2 mb-1",
11188
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.AlertCircleIcon, {
11189
+ size: _rebasepro_ui.iconSize.smallest,
11190
+ className: "text-amber-600 dark:text-amber-400"
11191
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
11192
+ variant: "caption",
11193
+ className: "font-semibold text-amber-700 dark:text-amber-400",
11194
+ children: "Copy your key now — it won't be shown again"
11195
+ })]
11196
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Typography, {
11197
+ variant: "caption",
11198
+ className: "text-amber-600 dark:text-amber-300",
11199
+ children: "This is the only time the full API key will be displayed. Store it securely."
11200
+ })]
11201
+ }),
11202
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11203
+ 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),
11204
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
11205
+ className: "flex-1 text-[12px] font-mono break-all text-surface-700 dark:text-surface-300 select-all",
11206
+ children: keyWithSecret.key
11207
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Tooltip, {
11208
+ title: copied ? "Copied!" : "Copy",
11209
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.IconButton, {
11210
+ size: "small",
11211
+ onClick: handleCopy,
11212
+ children: copied ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CheckCircleIcon, {
11213
+ size: _rebasepro_ui.iconSize.smallest,
11214
+ className: "text-emerald-500"
11215
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CopyIcon, { size: _rebasepro_ui.iconSize.smallest })
11216
+ })
11217
+ })]
11218
+ }),
11219
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
11220
+ className: "mt-4 space-y-1",
11221
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
11222
+ variant: "caption",
11223
+ color: "secondary",
11224
+ children: [
11225
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Name:" }),
11226
+ " ",
11227
+ keyWithSecret.name
11228
+ ]
11229
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(_rebasepro_ui.Typography, {
11230
+ variant: "caption",
11231
+ color: "secondary",
11232
+ children: [
11233
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", { children: "Permissions:" }),
11234
+ " ",
11235
+ permissionSummary(keyWithSecret.permissions)
11236
+ ]
11237
+ })]
11238
+ })
11239
+ ] }),
11240
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.DialogActions, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.Button, {
11241
+ color: "primary",
11242
+ onClick: onClose,
11243
+ children: "Done"
11244
+ }) })
11245
+ ]
11246
+ });
11247
+ }
11248
+ var init_ApiKeysView = __esmMin((() => {}));
11249
+ //#endregion
10391
11250
  //#region src/components/RebaseStudio.tsx
10392
11251
  var SQLEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_SQLEditor(), SQLEditor_exports)).then((m) => ({ default: m.SQLEditor })));
10393
11252
  var JSEditor = (0, react.lazy)(() => Promise.resolve().then(() => (init_JSEditor(), JSEditor_exports)).then((m) => ({ default: m.JSEditor })));
@@ -10398,6 +11257,7 @@ return result;
10398
11257
  var BranchesView = (0, react.lazy)(() => Promise.resolve().then(() => (init_BranchesView(), BranchesView_exports)).then((m) => ({ default: m.BranchesView })));
10399
11258
  var ApiExplorer = (0, react.lazy)(() => Promise.resolve().then(() => (init_ApiExplorer(), ApiExplorer_exports)).then((m) => ({ default: m.ApiExplorer })));
10400
11259
  var LogsExplorer = (0, react.lazy)(() => Promise.resolve().then(() => (init_LogsExplorer(), LogsExplorer_exports)).then((m) => ({ default: m.LogsExplorer })));
11260
+ var ApiKeysView = (0, react.lazy)(() => Promise.resolve().then(() => (init_ApiKeysView(), ApiKeysView_exports)).then((m) => ({ default: m.ApiKeysView })));
10401
11261
  /**
10402
11262
  * Declarative component to configure the Studio in Rebase.
10403
11263
  * Renders nothing — purely registers config into the RebaseRegistry.
@@ -10421,7 +11281,8 @@ return result;
10421
11281
  "schema-visualizer",
10422
11282
  "branches",
10423
11283
  "api",
10424
- "logs"
11284
+ "logs",
11285
+ "api-keys"
10425
11286
  ];
10426
11287
  const suspense = (el) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Suspense, {
10427
11288
  fallback: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_rebasepro_ui.CircularProgressCenter, {}),
@@ -10499,6 +11360,14 @@ return result;
10499
11360
  description: "Real-time system and query logs",
10500
11361
  view: suspense(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(LogsExplorer, {}))
10501
11362
  });
11363
+ if (activeTools.includes("api-keys")) views.push({
11364
+ slug: "api-keys",
11365
+ name: "API Keys",
11366
+ group: "Access Control",
11367
+ icon: "KeyRound",
11368
+ description: "Create and manage scoped API keys",
11369
+ view: suspense(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ApiKeysView, {}))
11370
+ });
10502
11371
  return views;
10503
11372
  }, [tools]);
10504
11373
  const homePageRef = react.default.useRef(resolvedHomePage);