@rebasepro/studio 0.9.1-canary.fd3754b → 0.10.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 (41) hide show
  1. package/dist/{ApiKeysView-Dbe5cXkS.js → ApiKeysView-vQKqt2wl.js} +44 -2
  2. package/dist/ApiKeysView-vQKqt2wl.js.map +1 -0
  3. package/dist/{JSEditor-Etgw9-Cw.js → JSEditor-BeHHuxhA.js} +3 -3
  4. package/dist/{JSEditor-Etgw9-Cw.js.map → JSEditor-BeHHuxhA.js.map} +1 -1
  5. package/dist/LogsExplorer-BCXoGREr.js +298 -0
  6. package/dist/LogsExplorer-BCXoGREr.js.map +1 -0
  7. package/dist/{RLSEditor-Cgv_kUVW.js → RLSEditor-rLlMcZPG.js} +42 -28
  8. package/dist/RLSEditor-rLlMcZPG.js.map +1 -0
  9. package/dist/{SQLEditor-ZZsy1Mk6.js → SQLEditor-_dCqVAN_.js} +3 -3
  10. package/dist/SQLEditor-_dCqVAN_.js.map +1 -0
  11. package/dist/{SchemaVisualizer-D01DzU-f.js → SchemaVisualizer-C_ErehfH.js} +2 -2
  12. package/dist/{SchemaVisualizer-D01DzU-f.js.map → SchemaVisualizer-C_ErehfH.js.map} +1 -1
  13. package/dist/components/RLSEditor/PolicyEditor.d.ts +1 -1
  14. package/dist/components/SQLEditor/SQLEditor.d.ts +2 -10
  15. package/dist/components/SQLEditor/SQLEditorSidebar.d.ts +1 -1
  16. package/dist/components/SQLEditor/SchemaBrowser.d.ts +1 -1
  17. package/dist/components/SQLEditor/sql_editor_types.d.ts +16 -0
  18. package/dist/index.es.js +58 -105
  19. package/dist/index.es.js.map +1 -1
  20. package/dist/utils/sql_utils.d.ts +1 -1
  21. package/package.json +13 -12
  22. package/src/components/ApiKeys/ApiKeysView.tsx +41 -1
  23. package/src/components/JSEditor/JSEditor.tsx +3 -3
  24. package/src/components/LogsExplorer/LogsExplorer.tsx +168 -46
  25. package/src/components/RLSEditor/PolicyEditor.tsx +15 -13
  26. package/src/components/RLSEditor/RLSEditor.tsx +48 -18
  27. package/src/components/SQLEditor/SQLEditor.tsx +3 -11
  28. package/src/components/SQLEditor/SQLEditorSidebar.tsx +1 -1
  29. package/src/components/SQLEditor/SchemaBrowser.tsx +3 -3
  30. package/src/components/SQLEditor/sql_editor_types.ts +18 -0
  31. package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +2 -1
  32. package/src/components/StudioHomePage.tsx +61 -86
  33. package/src/utils/sql_utils.test.ts +1 -1
  34. package/src/utils/sql_utils.ts +1 -1
  35. package/dist/ApiKeysView-Dbe5cXkS.js.map +0 -1
  36. package/dist/LogsExplorer-J4xfsuv3.js +0 -206
  37. package/dist/LogsExplorer-J4xfsuv3.js.map +0 -1
  38. package/dist/RLSEditor-Cgv_kUVW.js.map +0 -1
  39. package/dist/SQLEditor-ZZsy1Mk6.js.map +0 -1
  40. package/dist/index.umd.js +0 -11709
  41. package/dist/index.umd.js.map +0 -1
@@ -0,0 +1,298 @@
1
+ import { useApiConfig } from "@rebasepro/app";
2
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
3
+ import { ArrowDownToLineIcon, Checkbox, Label, Select, SelectItem, TextField, Typography, cls, defaultBorderMixin } from "@rebasepro/ui";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/LogsExplorer/LogsExplorer.tsx
6
+ var LEVEL_COLORS = {
7
+ debug: "text-surface-500",
8
+ info: "text-blue-600 dark:text-blue-500",
9
+ warn: "text-amber-600 dark:text-amber-500",
10
+ error: "text-red-600 dark:text-red-500"
11
+ };
12
+ var SOURCE_COLORS = {
13
+ api: "text-sky-600 dark:text-sky-400",
14
+ auth: "text-purple-600 dark:text-purple-400",
15
+ storage: "text-green-600 dark:text-green-500",
16
+ realtime: "text-orange-600 dark:text-orange-400",
17
+ system: "text-surface-600 dark:text-surface-400"
18
+ };
19
+ var idNum = (entry) => Number(entry.id.slice(4));
20
+ var sameLogs = (a, b) => a.length === b.length && a[0]?.id === b[0]?.id && a[a.length - 1]?.id === b[b.length - 1]?.id;
21
+ var STICK_THRESHOLD = 40;
22
+ function LogsExplorer() {
23
+ const [logs, setLogs] = useState([]);
24
+ const [level, setLevel] = useState("all");
25
+ const [source, setSource] = useState("all");
26
+ const [searchInput, setSearchInput] = useState("");
27
+ const [search, setSearch] = useState("");
28
+ const [autoScroll, setAutoScroll] = useState(true);
29
+ const [error, setError] = useState(null);
30
+ const [newCount, setNewCount] = useState(0);
31
+ const [atBottom, setAtBottom] = useState(true);
32
+ const containerRef = useRef(null);
33
+ const stickRef = useRef(true);
34
+ const lastMaxIdRef = useRef(null);
35
+ const apiConfig = useApiConfig();
36
+ useEffect(() => {
37
+ const t = setTimeout(() => setSearch(searchInput), 300);
38
+ return () => clearTimeout(t);
39
+ }, [searchInput]);
40
+ const fetchLogs = useCallback(async () => {
41
+ if (!apiConfig?.apiUrl) {
42
+ setError("No API URL configured — cannot load logs.");
43
+ return;
44
+ }
45
+ try {
46
+ const params = new URLSearchParams();
47
+ if (level && level !== "all") params.set("level", level);
48
+ if (source && source !== "all") params.set("source", source);
49
+ if (search) params.set("search", search);
50
+ params.set("limit", "200");
51
+ const headers = {};
52
+ const token = apiConfig.getAuthToken ? await apiConfig.getAuthToken() : null;
53
+ if (token) headers["Authorization"] = `Bearer ${token}`;
54
+ const resp = await fetch(`${apiConfig.apiUrl}/api/logs?${params}`, { headers });
55
+ if (!resp.ok) {
56
+ setError(resp.status === 401 || resp.status === 403 ? "Not authorised to read logs — an admin role is required." : `Could not load logs (HTTP ${resp.status}).`);
57
+ return;
58
+ }
59
+ const entries = ((await resp.json()).entries || []).slice().reverse();
60
+ const prevMax = lastMaxIdRef.current;
61
+ if (prevMax != null && !stickRef.current) {
62
+ const fresh = entries.filter((e) => idNum(e) > prevMax).length;
63
+ if (fresh > 0) setNewCount((c) => c + fresh);
64
+ }
65
+ if (entries.length > 0) lastMaxIdRef.current = idNum(entries[entries.length - 1]);
66
+ setLogs((prev) => sameLogs(prev, entries) ? prev : entries);
67
+ setError(null);
68
+ } catch (e) {
69
+ setError(e instanceof Error ? e.message : "Could not load logs.");
70
+ }
71
+ }, [
72
+ level,
73
+ source,
74
+ search,
75
+ apiConfig
76
+ ]);
77
+ useEffect(() => {
78
+ lastMaxIdRef.current = null;
79
+ setNewCount(0);
80
+ }, [
81
+ level,
82
+ source,
83
+ search
84
+ ]);
85
+ useEffect(() => {
86
+ let timeoutId = null;
87
+ let cancelled = false;
88
+ fetchLogs();
89
+ const scheduleNext = () => {
90
+ if (cancelled) return;
91
+ timeoutId = setTimeout(async () => {
92
+ if (document.visibilityState === "visible") await fetchLogs();
93
+ scheduleNext();
94
+ }, 3e3);
95
+ };
96
+ scheduleNext();
97
+ const handleVisibility = () => {
98
+ if (document.visibilityState === "visible") fetchLogs();
99
+ };
100
+ document.addEventListener("visibilitychange", handleVisibility);
101
+ return () => {
102
+ cancelled = true;
103
+ if (timeoutId) clearTimeout(timeoutId);
104
+ document.removeEventListener("visibilitychange", handleVisibility);
105
+ };
106
+ }, [fetchLogs]);
107
+ const scrollToBottom = useCallback(() => {
108
+ const el = containerRef.current;
109
+ if (!el) return;
110
+ el.scrollTop = el.scrollHeight;
111
+ stickRef.current = true;
112
+ setAtBottom(true);
113
+ setNewCount(0);
114
+ }, []);
115
+ const handleScroll = useCallback(() => {
116
+ const el = containerRef.current;
117
+ if (!el) return;
118
+ const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < STICK_THRESHOLD;
119
+ stickRef.current = nearBottom;
120
+ setAtBottom(nearBottom);
121
+ if (nearBottom) setNewCount(0);
122
+ }, []);
123
+ useLayoutEffect(() => {
124
+ if (autoScroll && stickRef.current) {
125
+ const el = containerRef.current;
126
+ if (el) el.scrollTop = el.scrollHeight;
127
+ }
128
+ }, [logs, autoScroll]);
129
+ return /* @__PURE__ */ jsxs("div", {
130
+ className: "flex flex-col h-[calc(100vh-64px)] w-full bg-surface-50 dark:bg-surface-800",
131
+ children: [
132
+ /* @__PURE__ */ jsxs("div", {
133
+ className: cls("flex gap-2 p-3 border-b items-center flex-wrap shrink-0", defaultBorderMixin),
134
+ children: [
135
+ /* @__PURE__ */ jsxs(Select, {
136
+ value: level,
137
+ onValueChange: setLevel,
138
+ size: "small",
139
+ placeholder: "All Levels",
140
+ children: [
141
+ /* @__PURE__ */ jsx(SelectItem, {
142
+ value: "all",
143
+ children: "All Levels"
144
+ }),
145
+ /* @__PURE__ */ jsx(SelectItem, {
146
+ value: "debug",
147
+ children: "Debug"
148
+ }),
149
+ /* @__PURE__ */ jsx(SelectItem, {
150
+ value: "info",
151
+ children: "Info"
152
+ }),
153
+ /* @__PURE__ */ jsx(SelectItem, {
154
+ value: "warn",
155
+ children: "Warn"
156
+ }),
157
+ /* @__PURE__ */ jsx(SelectItem, {
158
+ value: "error",
159
+ children: "Error"
160
+ })
161
+ ]
162
+ }),
163
+ /* @__PURE__ */ jsxs(Select, {
164
+ value: source,
165
+ onValueChange: setSource,
166
+ size: "small",
167
+ placeholder: "All Sources",
168
+ children: [
169
+ /* @__PURE__ */ jsx(SelectItem, {
170
+ value: "all",
171
+ children: "All Sources"
172
+ }),
173
+ /* @__PURE__ */ jsx(SelectItem, {
174
+ value: "api",
175
+ children: "API"
176
+ }),
177
+ /* @__PURE__ */ jsx(SelectItem, {
178
+ value: "auth",
179
+ children: "Auth"
180
+ }),
181
+ /* @__PURE__ */ jsx(SelectItem, {
182
+ value: "storage",
183
+ children: "Storage"
184
+ }),
185
+ /* @__PURE__ */ jsx(SelectItem, {
186
+ value: "realtime",
187
+ children: "Realtime"
188
+ }),
189
+ /* @__PURE__ */ jsx(SelectItem, {
190
+ value: "system",
191
+ children: "System"
192
+ })
193
+ ]
194
+ }),
195
+ /* @__PURE__ */ jsx(TextField, {
196
+ size: "small",
197
+ placeholder: "Search logs...",
198
+ value: searchInput,
199
+ onChange: (e) => setSearchInput(e.target.value),
200
+ className: "flex-1 min-w-[200px]"
201
+ }),
202
+ /* @__PURE__ */ jsxs("div", {
203
+ className: "flex items-center gap-1.5 cursor-pointer ml-2",
204
+ children: [/* @__PURE__ */ jsx(Checkbox, {
205
+ id: "auto-scroll",
206
+ checked: autoScroll,
207
+ onCheckedChange: (checked) => {
208
+ setAutoScroll(checked);
209
+ if (checked) scrollToBottom();
210
+ },
211
+ size: "small",
212
+ padding: false
213
+ }), /* @__PURE__ */ jsx(Label, {
214
+ htmlFor: "auto-scroll",
215
+ className: "text-xs select-none cursor-pointer text-surface-600 dark:text-surface-400",
216
+ children: "Auto-scroll"
217
+ })]
218
+ }),
219
+ /* @__PURE__ */ jsx("div", {
220
+ className: "ml-auto pl-4",
221
+ children: /* @__PURE__ */ jsxs(Typography, {
222
+ variant: "caption",
223
+ color: "secondary",
224
+ children: [logs.length, " entries"]
225
+ })
226
+ })
227
+ ]
228
+ }),
229
+ error && logs.length > 0 && /* @__PURE__ */ jsx("div", {
230
+ className: cls("px-4 py-1.5 border-b bg-amber-50 dark:bg-amber-950/20 shrink-0", defaultBorderMixin),
231
+ children: /* @__PURE__ */ jsx(Typography, {
232
+ variant: "caption",
233
+ className: "text-amber-700 dark:text-amber-400",
234
+ children: error
235
+ })
236
+ }),
237
+ /* @__PURE__ */ jsxs("div", {
238
+ className: "relative flex-1 min-h-0",
239
+ children: [/* @__PURE__ */ jsxs("div", {
240
+ ref: containerRef,
241
+ onScroll: handleScroll,
242
+ className: "h-full overflow-auto py-2",
243
+ children: [logs.map((log) => /* @__PURE__ */ jsxs("div", {
244
+ className: cls("flex gap-4 px-4 py-[6px] border-b hover:bg-surface-100 dark:hover:bg-surface-900 transition-colors", defaultBorderMixin),
245
+ children: [
246
+ /* @__PURE__ */ jsx(Typography, {
247
+ variant: "body2",
248
+ color: "secondary",
249
+ className: "w-[72px] shrink-0 font-mono",
250
+ children: new Date(log.timestamp).toLocaleTimeString()
251
+ }),
252
+ /* @__PURE__ */ jsx(Typography, {
253
+ variant: "body2",
254
+ className: cls("w-[48px] shrink-0 uppercase font-semibold font-mono", LEVEL_COLORS[log.level] || "text-surface-500"),
255
+ children: log.level
256
+ }),
257
+ /* @__PURE__ */ jsxs(Typography, {
258
+ variant: "body2",
259
+ className: cls("w-[80px] shrink-0 font-mono", SOURCE_COLORS[log.source] || "text-surface-500"),
260
+ children: [
261
+ "[",
262
+ log.source,
263
+ "]"
264
+ ]
265
+ }),
266
+ /* @__PURE__ */ jsx(Typography, {
267
+ variant: "body2",
268
+ className: "flex-1 font-mono break-all whitespace-pre-wrap text-surface-900 dark:text-surface-100",
269
+ children: log.message
270
+ })
271
+ ]
272
+ }, log.id)), logs.length === 0 && /* @__PURE__ */ jsx("div", {
273
+ className: "p-8 text-center",
274
+ children: /* @__PURE__ */ jsx(Typography, {
275
+ variant: "body2",
276
+ className: error ? "text-red-600 dark:text-red-500" : void 0,
277
+ color: error ? void 0 : "secondary",
278
+ children: error ?? "No log entries yet. Logs will appear here as requests come in."
279
+ })
280
+ })]
281
+ }), autoScroll && !atBottom && newCount > 0 && /* @__PURE__ */ jsxs("button", {
282
+ onClick: scrollToBottom,
283
+ className: "absolute bottom-3 left-1/2 -translate-x-1/2 flex items-center gap-1.5 rounded-full bg-primary text-white text-xs font-medium pl-2.5 pr-3 py-1.5 shadow-md hover:bg-primary-dark transition-colors",
284
+ children: [
285
+ /* @__PURE__ */ jsx(ArrowDownToLineIcon, { size: 14 }),
286
+ newCount,
287
+ " new ",
288
+ newCount === 1 ? "entry" : "entries"
289
+ ]
290
+ })]
291
+ })
292
+ ]
293
+ });
294
+ }
295
+ //#endregion
296
+ export { LogsExplorer };
297
+
298
+ //# sourceMappingURL=LogsExplorer-BCXoGREr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LogsExplorer-BCXoGREr.js","names":[],"sources":["../src/components/LogsExplorer/LogsExplorer.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { ArrowDownToLineIcon, Checkbox, cls, defaultBorderMixin, Label, Select, SelectItem, TextField, Typography } from \"@rebasepro/ui\";\nimport { useApiConfig } from \"@rebasepro/app\";\n\ninterface LogEntry {\n id: string;\n timestamp: string;\n level: \"debug\" | \"info\" | \"warn\" | \"error\";\n source: \"api\" | \"auth\" | \"storage\" | \"realtime\" | \"system\";\n message: string;\n metadata?: Record<string, unknown>;\n}\n\nconst LEVEL_COLORS: Record<string, string> = {\n debug: \"text-surface-500\",\n info: \"text-blue-600 dark:text-blue-500\",\n warn: \"text-amber-600 dark:text-amber-500\",\n error: \"text-red-600 dark:text-red-500\"\n};\n\nconst SOURCE_COLORS: Record<string, string> = {\n api: \"text-sky-600 dark:text-sky-400\",\n auth: \"text-purple-600 dark:text-purple-400\",\n storage: \"text-green-600 dark:text-green-500\",\n realtime: \"text-orange-600 dark:text-orange-400\",\n system: \"text-surface-600 dark:text-surface-400\"\n};\n\n// Ids are `log_<n>` with a monotonic counter, so numeric comparison tells\n// old entries from new ones across polls.\nconst idNum = (entry: LogEntry): number => Number(entry.id.slice(\"log_\".length));\n\n// The window is contiguous and ordered, so identical ends mean identical content.\nconst sameLogs = (a: LogEntry[], b: LogEntry[]): boolean =>\n a.length === b.length &&\n a[0]?.id === b[0]?.id &&\n a[a.length - 1]?.id === b[b.length - 1]?.id;\n\n// How close to the bottom edge still counts as \"at the bottom\".\nconst STICK_THRESHOLD = 40;\n\nexport function LogsExplorer() {\n const [logs, setLogs] = useState<LogEntry[]>([]);\n const [level, setLevel] = useState<string>(\"all\");\n const [source, setSource] = useState<string>(\"all\");\n const [searchInput, setSearchInput] = useState(\"\");\n const [search, setSearch] = useState(\"\");\n const [autoScroll, setAutoScroll] = useState(true);\n const [error, setError] = useState<string | null>(null);\n // Entries that arrived while the user was scrolled away from the bottom.\n const [newCount, setNewCount] = useState(0);\n const [atBottom, setAtBottom] = useState(true);\n const containerRef = useRef<HTMLDivElement>(null);\n // Whether the view is stuck to the bottom right now. A ref, not state:\n // the scroll handler and the fetch loop both read it synchronously.\n const stickRef = useRef(true);\n const lastMaxIdRef = useRef<number | null>(null);\n const apiConfig = useApiConfig();\n\n // Debounce the search box so typing doesn't refetch per keystroke.\n useEffect(() => {\n const t = setTimeout(() => setSearch(searchInput), 300);\n return () => clearTimeout(t);\n }, [searchInput]);\n\n const fetchLogs = useCallback(async () => {\n if (!apiConfig?.apiUrl) {\n setError(\"No API URL configured — cannot load logs.\");\n return;\n }\n try {\n const params = new URLSearchParams();\n if (level && level !== \"all\") params.set(\"level\", level);\n if (source && source !== \"all\") params.set(\"source\", source);\n if (search) params.set(\"search\", search);\n params.set(\"limit\", \"200\");\n\n // Logs are admin-only, so the request must carry the auth token. The\n // URL is absolute: a relative one would resolve against the frontend\n // origin, which serves index.html rather than the API.\n const headers: Record<string, string> = {};\n const token = apiConfig.getAuthToken ? await apiConfig.getAuthToken() : null;\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n\n const resp = await fetch(`${apiConfig.apiUrl}/api/logs?${params}`, { headers });\n if (!resp.ok) {\n setError(resp.status === 401 || resp.status === 403\n ? \"Not authorised to read logs — an admin role is required.\"\n : `Could not load logs (HTTP ${resp.status}).`);\n return;\n }\n const data: { entries?: LogEntry[] } = await resp.json();\n // The API returns newest-first; the view tails like a terminal, so\n // flip to chronological order (newest at the bottom).\n const entries = (data.entries || []).slice().reverse();\n\n const prevMax = lastMaxIdRef.current;\n if (prevMax != null && !stickRef.current) {\n const fresh = entries.filter(e => idNum(e) > prevMax).length;\n if (fresh > 0) setNewCount(c => c + fresh);\n }\n if (entries.length > 0) lastMaxIdRef.current = idNum(entries[entries.length - 1]);\n\n // Unchanged window → keep the previous array so React leaves the\n // DOM alone (hover states and text selection survive the poll).\n setLogs(prev => sameLogs(prev, entries) ? prev : entries);\n setError(null);\n } catch (e) {\n setError(e instanceof Error ? e.message : \"Could not load logs.\");\n }\n }, [level, source, search, apiConfig]);\n\n // A filter change replaces the window wholesale — \"new entries since last\n // poll\" stops meaning anything, so the counter starts over.\n useEffect(() => {\n lastMaxIdRef.current = null;\n setNewCount(0);\n }, [level, source, search]);\n\n useEffect(() => {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n let cancelled = false;\n\n fetchLogs();\n\n const scheduleNext = () => {\n if (cancelled) return;\n timeoutId = setTimeout(async () => {\n if (document.visibilityState === \"visible\") {\n await fetchLogs();\n }\n scheduleNext();\n }, 3000);\n };\n\n scheduleNext();\n\n const handleVisibility = () => {\n if (document.visibilityState === \"visible\") {\n fetchLogs();\n }\n };\n document.addEventListener(\"visibilitychange\", handleVisibility);\n\n return () => {\n cancelled = true;\n if (timeoutId) clearTimeout(timeoutId);\n document.removeEventListener(\"visibilitychange\", handleVisibility);\n };\n }, [fetchLogs]);\n\n const scrollToBottom = useCallback(() => {\n const el = containerRef.current;\n if (!el) return;\n el.scrollTop = el.scrollHeight;\n stickRef.current = true;\n setAtBottom(true);\n setNewCount(0);\n }, []);\n\n // Stick to the bottom only while the user is already there. Scrolling up\n // disengages; scrolling back down re-engages. Our own scrollTop writes\n // always land at the bottom, so they can never disengage it.\n const handleScroll = useCallback(() => {\n const el = containerRef.current;\n if (!el) return;\n const nearBottom = el.scrollHeight - el.scrollTop - el.clientHeight < STICK_THRESHOLD;\n stickRef.current = nearBottom;\n setAtBottom(nearBottom);\n if (nearBottom) setNewCount(0);\n }, []);\n\n useLayoutEffect(() => {\n if (autoScroll && stickRef.current) {\n const el = containerRef.current;\n if (el) el.scrollTop = el.scrollHeight;\n }\n }, [logs, autoScroll]);\n\n return (\n <div className=\"flex flex-col h-[calc(100vh-64px)] w-full bg-surface-50 dark:bg-surface-800\">\n {/* Toolbar */}\n <div className={cls(\n \"flex gap-2 p-3 border-b items-center flex-wrap shrink-0\",\n defaultBorderMixin\n )}>\n <Select\n value={level}\n onValueChange={setLevel}\n size=\"small\"\n placeholder=\"All Levels\"\n >\n <SelectItem value=\"all\">All Levels</SelectItem>\n <SelectItem value=\"debug\">Debug</SelectItem>\n <SelectItem value=\"info\">Info</SelectItem>\n <SelectItem value=\"warn\">Warn</SelectItem>\n <SelectItem value=\"error\">Error</SelectItem>\n </Select>\n <Select\n value={source}\n onValueChange={setSource}\n size=\"small\"\n placeholder=\"All Sources\"\n >\n <SelectItem value=\"all\">All Sources</SelectItem>\n <SelectItem value=\"api\">API</SelectItem>\n <SelectItem value=\"auth\">Auth</SelectItem>\n <SelectItem value=\"storage\">Storage</SelectItem>\n <SelectItem value=\"realtime\">Realtime</SelectItem>\n <SelectItem value=\"system\">System</SelectItem>\n </Select>\n <TextField\n size=\"small\"\n placeholder=\"Search logs...\"\n value={searchInput}\n onChange={e => setSearchInput(e.target.value)}\n className=\"flex-1 min-w-[200px]\"\n />\n <div className=\"flex items-center gap-1.5 cursor-pointer ml-2\">\n <Checkbox\n id=\"auto-scroll\"\n checked={autoScroll}\n onCheckedChange={(checked: boolean) => {\n setAutoScroll(checked);\n if (checked) scrollToBottom();\n }}\n size=\"small\"\n padding={false}\n />\n <Label\n htmlFor=\"auto-scroll\"\n className=\"text-xs select-none cursor-pointer text-surface-600 dark:text-surface-400\"\n >\n Auto-scroll\n </Label>\n </div>\n <div className=\"ml-auto pl-4\">\n <Typography variant=\"caption\" color=\"secondary\">\n {logs.length} entries\n </Typography>\n </div>\n </div>\n\n {/* A failing poll must stay visible even while stale logs are on\n screen — otherwise the view quietly freezes. */}\n {error && logs.length > 0 && (\n <div className={cls(\n \"px-4 py-1.5 border-b bg-amber-50 dark:bg-amber-950/20 shrink-0\",\n defaultBorderMixin\n )}>\n <Typography variant=\"caption\" className=\"text-amber-700 dark:text-amber-400\">\n {error}\n </Typography>\n </div>\n )}\n\n {/* Log entries */}\n <div className=\"relative flex-1 min-h-0\">\n <div\n ref={containerRef}\n onScroll={handleScroll}\n className=\"h-full overflow-auto py-2\"\n >\n {logs.map(log => (\n <div\n key={log.id}\n className={cls(\n \"flex gap-4 px-4 py-[6px] border-b hover:bg-surface-100 dark:hover:bg-surface-900 transition-colors\",\n defaultBorderMixin\n )}\n >\n <Typography variant=\"body2\" color=\"secondary\" className=\"w-[72px] shrink-0 font-mono\">\n {new Date(log.timestamp).toLocaleTimeString()}\n </Typography>\n <Typography variant=\"body2\" className={cls(\"w-[48px] shrink-0 uppercase font-semibold font-mono\", LEVEL_COLORS[log.level] || \"text-surface-500\")}>\n {log.level}\n </Typography>\n <Typography variant=\"body2\" className={cls(\"w-[80px] shrink-0 font-mono\", SOURCE_COLORS[log.source] || \"text-surface-500\")}>\n [{log.source}]\n </Typography>\n <Typography variant=\"body2\" className=\"flex-1 font-mono break-all whitespace-pre-wrap text-surface-900 dark:text-surface-100\">\n {log.message}\n </Typography>\n </div>\n ))}\n {logs.length === 0 && (\n <div className=\"p-8 text-center\">\n <Typography\n variant=\"body2\"\n className={error ? \"text-red-600 dark:text-red-500\" : undefined}\n color={error ? undefined : \"secondary\"}\n >\n {error ?? \"No log entries yet. Logs will appear here as requests come in.\"}\n </Typography>\n </div>\n )}\n </div>\n\n {autoScroll && !atBottom && newCount > 0 && (\n <button\n onClick={scrollToBottom}\n className=\"absolute bottom-3 left-1/2 -translate-x-1/2 flex items-center gap-1.5 rounded-full bg-primary text-white text-xs font-medium pl-2.5 pr-3 py-1.5 shadow-md hover:bg-primary-dark transition-colors\"\n >\n <ArrowDownToLineIcon size={14}/>\n {newCount} new {newCount === 1 ? \"entry\" : \"entries\"}\n </button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -4,7 +4,8 @@ import { useCallback, useEffect, useMemo, useState } from "react";
4
4
  import { Alert, AlertTriangleIcon, Button, Chip, CircularProgress, DatabaseIcon, Dialog, DialogActions, DialogContent, DialogTitle, HelpCircleIcon, IconButton, KeyIcon, Link2Icon, LockIcon, MultiSelect, MultiSelectItem, Paper, RefreshCwIcon, ResizablePanels, Select, SelectItem, ShieldIcon, Tab, Tabs, TextField, Tooltip, Trash2Icon, Typography, cls, defaultBorderMixin, iconSize } from "@rebasepro/ui";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { isPostgresCollectionConfig } from "@rebasepro/types";
7
- import { JUNCTION_TABLES_SQL, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS } from "@rebasepro/common";
7
+ import { getPolicyNamesForRule, getPolicyNamesForRules, getPolicyOperations } from "@rebasepro/utils";
8
+ import { JUNCTION_TABLES_SQL, REBASE_INTERNAL_PREFIXES, REBASE_INTERNAL_SCHEMAS, getJunctionSecurityRules, resolveJunctionSpecs } from "@rebasepro/common";
8
9
  //#region src/components/RLSEditor/PolicyEditor.tsx
9
10
  var COMMAND_OPTIONS = [
10
11
  "ALL",
@@ -56,34 +57,34 @@ var POLICY_PRESETS = [
56
57
  {
57
58
  id: "user_select_own",
58
59
  label: "Users can read their own rows",
59
- description: "Users can only read rows where the user_id matches their auth.uid()",
60
+ description: "Users can only read rows where the uid matches their auth.uid()",
60
61
  policyname: "Users can select their own data",
61
62
  cmd: "SELECT",
62
63
  permissive: "PERMISSIVE",
63
64
  roles: ["authenticated"],
64
- qual: "auth.uid() = user_id",
65
+ qual: "auth.uid() = uid",
65
66
  with_check: ""
66
67
  },
67
68
  {
68
69
  id: "user_update_own",
69
70
  label: "Users can update their own rows",
70
- description: "Users can only update rows where the user_id matches their auth.uid()",
71
+ description: "Users can only update rows where the uid matches their auth.uid()",
71
72
  policyname: "Users can update their own data",
72
73
  cmd: "UPDATE",
73
74
  permissive: "PERMISSIVE",
74
75
  roles: ["authenticated"],
75
- qual: "auth.uid() = user_id",
76
- with_check: "auth.uid() = user_id"
76
+ qual: "auth.uid() = uid",
77
+ with_check: "auth.uid() = uid"
77
78
  },
78
79
  {
79
80
  id: "user_delete_own",
80
81
  label: "Users can delete their own rows",
81
- description: "Users can only delete rows where the user_id matches their auth.uid()",
82
+ description: "Users can only delete rows where the uid matches their auth.uid()",
82
83
  policyname: "Users can delete their own data",
83
84
  cmd: "DELETE",
84
85
  permissive: "PERMISSIVE",
85
86
  roles: ["authenticated"],
86
- qual: "auth.uid() = user_id",
87
+ qual: "auth.uid() = uid",
87
88
  with_check: ""
88
89
  }
89
90
  ];
@@ -138,9 +139,10 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
138
139
  });
139
140
  };
140
141
  return /* @__PURE__ */ jsxs(Fragment, { children: [
141
- /* @__PURE__ */ jsxs(DialogTitle, {
142
- className: "flex justify-between items-center w-full",
142
+ /* @__PURE__ */ jsxs(Typography, {
143
143
  variant: "h6",
144
+ gutterBottom: true,
145
+ className: "mt-8 mx-8 flex justify-between items-center w-full",
144
146
  children: [/* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", { children: policy ? t("studio_policy_edit") : t("studio_policy_create") }), /* @__PURE__ */ jsxs("div", {
145
147
  className: "text-sm font-normal text-text-secondary dark:text-text-secondary-dark tracking-wide mt-1",
146
148
  children: [
@@ -455,7 +457,7 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
455
457
  }),
456
458
  /* @__PURE__ */ jsx("div", {
457
459
  className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
458
- children: "Example: auth.uid() = user_id"
460
+ children: "Example: auth.uid() = uid"
459
461
  }),
460
462
  /* @__PURE__ */ jsx(Typography, {
461
463
  variant: "caption",
@@ -479,7 +481,7 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
479
481
  }),
480
482
  /* @__PURE__ */ jsx("div", {
481
483
  className: cls("bg-surface-100 dark:bg-surface-950 px-3 py-2 rounded-md font-mono text-sm my-2", defaultBorderMixin),
482
- children: "Example: auth.uid() = user_id"
484
+ children: "Example: auth.uid() = uid"
483
485
  }),
484
486
  /* @__PURE__ */ jsx(Typography, {
485
487
  variant: "caption",
@@ -511,7 +513,7 @@ var PolicyEditor = ({ policy, schema, table, onSave, onCancel }) => {
511
513
  className: "block mt-0.5",
512
514
  children: ["Returns the current user's ID as text. Example: ", /* @__PURE__ */ jsx("code", {
513
515
  className: "bg-surface-100 dark:bg-surface-950 px-1 py-0.5 rounded text-[11px]",
514
- children: "auth.uid() = user_id"
516
+ children: "auth.uid() = uid"
515
517
  })]
516
518
  })] }),
517
519
  /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx("code", {
@@ -838,21 +840,33 @@ var RLSEditor = ({ apiUrl = "" }) => {
838
840
  };
839
841
  });
840
842
  if (activeCollection && isPostgresCollectionConfig(activeCollection) && activeCollection.securityRules) activeCollection.securityRules.forEach((rule) => {
841
- const ruleName = rule.name;
842
- if (!ruleName) return;
843
- policiesMap[ruleName] = {
844
- policyname: ruleName,
845
- tablename: activeTableData.tableName,
846
- permissive: (rule.mode || "permissive").toUpperCase(),
847
- cmd: (rule.operation || "ALL").toUpperCase(),
848
- roles: [...rule.roles ?? ["public"]],
849
- qual: rule.using || null,
850
- with_check: rule.withCheck || null,
851
- status: policiesMap[ruleName] ? "both" : "code_only"
852
- };
843
+ const ops = getPolicyOperations(rule);
844
+ getPolicyNamesForRule(rule, activeTableData.tableName).forEach((policyName, opIdx) => {
845
+ policiesMap[policyName] = {
846
+ policyname: policyName,
847
+ tablename: activeTableData.tableName,
848
+ permissive: (rule.mode || "permissive").toUpperCase(),
849
+ cmd: (ops[opIdx] ?? rule.operation ?? "ALL").toUpperCase(),
850
+ roles: [...rule.roles ?? ["public"]],
851
+ qual: rule.using || null,
852
+ with_check: rule.withCheck || null,
853
+ status: policiesMap[policyName] ? "both" : "code_only"
854
+ };
855
+ });
853
856
  });
857
+ if (!activeCollection) try {
858
+ const spec = resolveJunctionSpecs(collectionRegistry.collections ?? []).get(activeTableData.tableName);
859
+ if (spec) {
860
+ const generatedNames = getPolicyNamesForRules(getJunctionSecurityRules(spec), spec.table);
861
+ for (const p of Object.values(policiesMap)) if (generatedNames.has(p.policyname)) p.status = "both";
862
+ }
863
+ } catch {}
854
864
  return Object.values(policiesMap).sort((a, b) => a.policyname.localeCompare(b.policyname));
855
- }, [activeTableData, activeCollection]);
865
+ }, [
866
+ activeTableData,
867
+ activeCollection,
868
+ collectionRegistry.collections
869
+ ]);
856
870
  const rlsStats = useMemo(() => {
857
871
  return {
858
872
  total: tables.length,
@@ -1321,7 +1335,7 @@ var RLSEditor = ({ apiUrl = "" }) => {
1321
1335
  }), /* @__PURE__ */ jsx(Typography, {
1322
1336
  variant: "caption",
1323
1337
  className: "opacity-80",
1324
- 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."
1338
+ children: "This is an auto-generated junction table for a many-to-many relation. Rebase derives its policies: rows are readable when both related rows are, and writable following the declaring collection's update rules (plus the server/admin baseline). You can still add RLS policies directly to broaden access."
1325
1339
  })] })]
1326
1340
  })
1327
1341
  }),
@@ -1543,4 +1557,4 @@ var RLSEditor = ({ apiUrl = "" }) => {
1543
1557
  //#endregion
1544
1558
  export { RLSEditor };
1545
1559
 
1546
- //# sourceMappingURL=RLSEditor-Cgv_kUVW.js.map
1560
+ //# sourceMappingURL=RLSEditor-rLlMcZPG.js.map