@rebasepro/studio 0.17.3 → 0.18.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 (81) hide show
  1. package/LICENSE +0 -1
  2. package/README.md +12 -4
  3. package/dist/{ApiKeysView-DiCurTEU.js → ApiKeysView-EIvegrUk.js} +54 -39
  4. package/dist/ApiKeysView-EIvegrUk.js.map +1 -0
  5. package/dist/{BackupsView-DNS6LdVg.js → BackupsView-DVkKzaPR.js} +29 -31
  6. package/dist/BackupsView-DVkKzaPR.js.map +1 -0
  7. package/dist/{BranchesView-CM3B-ER3.js → BranchesView-Df8hQ8wE.js} +85 -24
  8. package/dist/BranchesView-Df8hQ8wE.js.map +1 -0
  9. package/dist/{CronJobsView-BHtJZeJZ.js → CronJobsView-CLOGjb-L.js} +32 -27
  10. package/dist/CronJobsView-CLOGjb-L.js.map +1 -0
  11. package/dist/{RLSEditor-CBX4uD66.js → RLSEditor-Sa1D34ny.js} +724 -615
  12. package/dist/RLSEditor-Sa1D34ny.js.map +1 -0
  13. package/dist/{SQLEditor-ByecwX8B.js → SQLEditor-BfNA8xM3.js} +9 -4
  14. package/dist/{SQLEditor-ByecwX8B.js.map → SQLEditor-BfNA8xM3.js.map} +1 -1
  15. package/dist/{SchemaVisualizer-D6tMZgeC.js → SchemaVisualizer-DTDMYL6o.js} +84 -110
  16. package/dist/SchemaVisualizer-DTDMYL6o.js.map +1 -0
  17. package/dist/{StorageView-BGDeC5GI.js → StorageView-Ed9Q_VlB.js} +5 -32
  18. package/dist/StorageView-Ed9Q_VlB.js.map +1 -0
  19. package/dist/components/RLSEditor/saveRules.d.ts +38 -0
  20. package/dist/components/RebaseStudio.d.ts +1 -1
  21. package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +5 -2
  22. package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +0 -4
  23. package/dist/components/load-failure-view.d.ts +17 -0
  24. package/dist/components/load-failure.d.ts +47 -0
  25. package/dist/index.es.js +56 -36
  26. package/dist/index.es.js.map +1 -1
  27. package/dist/load-failure-C6OMJSGC.js +31 -0
  28. package/dist/load-failure-C6OMJSGC.js.map +1 -0
  29. package/dist/load-failure-view-CBTB_h-h.js +48 -0
  30. package/dist/load-failure-view-CBTB_h-h.js.map +1 -0
  31. package/package.json +36 -21
  32. package/dist/ApiKeysView-DiCurTEU.js.map +0 -1
  33. package/dist/BackupsView-DNS6LdVg.js.map +0 -1
  34. package/dist/BranchesView-CM3B-ER3.js.map +0 -1
  35. package/dist/CronJobsView-BHtJZeJZ.js.map +0 -1
  36. package/dist/RLSEditor-CBX4uD66.js.map +0 -1
  37. package/dist/SchemaVisualizer-D6tMZgeC.js.map +0 -1
  38. package/dist/StorageView-BGDeC5GI.js.map +0 -1
  39. package/dist/components/StorageView/storage-failure.d.ts +0 -34
  40. package/src/components/ApiExplorer/ApiExplorer.tsx +0 -299
  41. package/src/components/ApiExplorer/EndpointDetail.tsx +0 -279
  42. package/src/components/ApiExplorer/TryItPanel.tsx +0 -499
  43. package/src/components/ApiExplorer/parseSpec.ts +0 -104
  44. package/src/components/ApiExplorer/types.ts +0 -84
  45. package/src/components/ApiKeys/ApiKeysView.tsx +0 -479
  46. package/src/components/ApiKeys/CreateApiKeyDialog.tsx +0 -711
  47. package/src/components/ApiKeys/permissions.ts +0 -145
  48. package/src/components/AuthSimulationSelector.tsx +0 -72
  49. package/src/components/Backups/BackupsView.tsx +0 -185
  50. package/src/components/Branches/BranchesView.tsx +0 -386
  51. package/src/components/CronJobs/CronJobsView.tsx +0 -413
  52. package/src/components/JSEditor/JSEditor.tsx +0 -1100
  53. package/src/components/JSEditor/JSEditorSidebar.tsx +0 -350
  54. package/src/components/JSEditor/JSMonacoEditor.tsx +0 -410
  55. package/src/components/LogsExplorer/LogsExplorer.tsx +0 -273
  56. package/src/components/LogsExplorer/useLogTail.ts +0 -356
  57. package/src/components/RLSEditor/PolicyEditor.tsx +0 -399
  58. package/src/components/RLSEditor/RLSEditor.tsx +0 -1128
  59. package/src/components/RLSEditor/index.ts +0 -1
  60. package/src/components/RLSEditor/policy-presets.ts +0 -176
  61. package/src/components/RLSEditor/saveSecurityRules.ts +0 -48
  62. package/src/components/RebaseStudio.tsx +0 -174
  63. package/src/components/SQLEditor/ExplainVisualizer.tsx +0 -128
  64. package/src/components/SQLEditor/MonacoEditor.tsx +0 -203
  65. package/src/components/SQLEditor/SQLEditor.tsx +0 -1518
  66. package/src/components/SQLEditor/SQLEditorSidebar.tsx +0 -173
  67. package/src/components/SQLEditor/SchemaBrowser.tsx +0 -168
  68. package/src/components/SQLEditor/sql_editor_types.ts +0 -18
  69. package/src/components/SchemaVisualizer/RelationEdge.tsx +0 -102
  70. package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +0 -710
  71. package/src/components/SchemaVisualizer/TableNode.tsx +0 -257
  72. package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +0 -140
  73. package/src/components/SchemaVisualizer/useLiveRls.ts +0 -86
  74. package/src/components/SchemaVisualizer/useSchemaGraph.ts +0 -426
  75. package/src/components/StorageView/StorageView.tsx +0 -1588
  76. package/src/components/StorageView/storage-failure.ts +0 -63
  77. package/src/components/StudioHomePage.tsx +0 -430
  78. package/src/index.ts +0 -31
  79. package/src/utils/parseSpec.test.ts +0 -309
  80. package/src/utils/sql_utils.test.ts +0 -265
  81. package/src/utils/sql_utils.ts +0 -291
@@ -1,350 +0,0 @@
1
-
2
- import React, { useState } from "react";
3
- import {
4
- cls,
5
- defaultBorderMixin,
6
- IconButton,
7
- iconSize,
8
- PlusIcon,
9
- Tab,
10
- Tabs,
11
- Tooltip,
12
- Trash2Icon,
13
- Typography
14
- } from "@rebasepro/ui";
15
-
16
- export interface JSSnippet {
17
- id: string;
18
- name: string;
19
- code: string;
20
- createdAt: number;
21
- isFavorite?: boolean;
22
- }
23
-
24
- interface CollectionInfo {
25
- slug: string;
26
- properties: string[];
27
- }
28
-
29
- interface JSEditorSidebarProps {
30
- collections: CollectionInfo[];
31
- snippets: JSSnippet[];
32
- history: string[];
33
- onSelectSnippet: (code: string) => void;
34
- onDeleteSnippet: (id: string) => void;
35
- onInsertCode: (code: string) => void;
36
- }
37
-
38
- const QUICK_REFERENCE: { label: string; code: string; description: string }[] = [
39
- {
40
- label: "Find all",
41
- code: "const result = await client.data.collection(\"COLLECTION\").find({ limit: 20 });\nreturn result;",
42
- description: "Fetch records with pagination"
43
- },
44
- {
45
- label: "Find by ID",
46
- code: "const item = await client.data.collection(\"COLLECTION\").findById(\"ID\");\nreturn item;",
47
- description: "Fetch a single record"
48
- },
49
- {
50
- label: "Create",
51
- code: "const created = await client.data.collection(\"COLLECTION\").create({\n // your fields here\n});\nreturn created;",
52
- description: "Insert a new record"
53
- },
54
- {
55
- label: "Update",
56
- code: "const updated = await client.data.collection(\"COLLECTION\").update(\"ID\", {\n // fields to update\n});\nreturn updated;",
57
- description: "Update an existing record"
58
- },
59
- {
60
- label: "Delete",
61
- code: "await client.data.collection(\"COLLECTION\").delete(\"ID\");\nreturn { success: true };",
62
- description: "Delete a record"
63
- },
64
- {
65
- label: "Fluent query",
66
- code: "const result = await client.data.collection(\"COLLECTION\")\n .where(\"field\", \">\", value)\n .orderBy(\"field\", \"desc\")\n .limit(10)\n .find();\nreturn result;",
67
- description: "Chained query builder"
68
- },
69
- {
70
- label: "Auth: Current session",
71
- code: "const session = client.auth.getSession();\nreturn session;",
72
- description: "Get current auth session"
73
- },
74
- {
75
- label: "Admin: List users",
76
- code: "const { users } = await client.admin.listUsers();\nreturn users;",
77
- description: "List all registered users"
78
- },
79
- {
80
- label: "Custom endpoint",
81
- code: "const result = await client.call(\"/my-endpoint\", { key: \"value\" });\nreturn result;",
82
- description: "Call a custom API endpoint"
83
- }
84
- ];
85
-
86
- export const JSEditorSidebar = ({
87
- collections,
88
- snippets,
89
- history,
90
- onSelectSnippet,
91
- onDeleteSnippet,
92
- onInsertCode
93
- }: JSEditorSidebarProps) => {
94
- const [activeTab, setActiveTab] = useState<"collections" | "snippets" | "history" | "reference">("collections");
95
-
96
- return (
97
- <div className={cls("flex flex-col h-full w-full bg-white dark:bg-surface-950 border-r", defaultBorderMixin)}>
98
- <Tabs value={activeTab} onValueChange={(v) => setActiveTab(v as "collections" | "snippets" | "history" | "reference")} variant="boxy" className="border-b border-surface-200 dark:border-surface-950">
99
- <Tab value="collections">
100
- <Tooltip title="Browse collections">
101
- <span className="text-xs">Collections</span>
102
- </Tooltip>
103
- </Tab>
104
- <Tab value="reference">
105
- <Tooltip title="SDK quick reference">
106
- <span className="text-xs">Reference</span>
107
- </Tooltip>
108
- </Tab>
109
- <Tab value="snippets">
110
- <span className="text-xs">Snippets</span>
111
- </Tab>
112
- <Tab value="history">
113
- <span className="text-xs">History</span>
114
- </Tab>
115
- </Tabs>
116
-
117
- <div className="flex-grow overflow-hidden relative">
118
- {/* Collections browser */}
119
- {activeTab === "collections" && (
120
- <div className="flex flex-col h-full">
121
- <div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
122
- <Typography variant="caption" className="font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark">
123
- Collections
124
- </Typography>
125
- </div>
126
- <div className="flex-grow overflow-y-auto p-1 space-y-1 no-scrollbar">
127
- {collections.length === 0 ? (
128
- <div className="p-4 text-center">
129
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark">
130
- No collections found
131
- </Typography>
132
- </div>
133
- ) : (
134
- collections.map(col => (
135
- <CollectionItem
136
- key={col.slug}
137
- collection={col}
138
- onInsertCode={onInsertCode}
139
- />
140
- ))
141
- )}
142
- </div>
143
- </div>
144
- )}
145
-
146
- {/* Quick reference */}
147
- {activeTab === "reference" && (
148
- <div className="flex flex-col h-full">
149
- <div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
150
- <Typography variant="caption" className="font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark">
151
- SDK Reference
152
- </Typography>
153
- </div>
154
- <div className="flex-grow overflow-y-auto p-2 space-y-2 no-scrollbar">
155
- {QUICK_REFERENCE.map((ref, i) => (
156
- <div
157
- key={i}
158
- className={cls(
159
- "group p-2.5 rounded-lg border hover:border-primary/40 dark:hover:border-primary/40",
160
- "bg-white dark:bg-surface-900 transition-all cursor-pointer",
161
- defaultBorderMixin
162
- )}
163
- onClick={() => onInsertCode(ref.code)}
164
- >
165
- <div className="flex items-center justify-between mb-1">
166
- <Typography variant="body2" className="text-text-primary dark:text-text-primary-dark font-semibold text-[13px]">
167
- {ref.label}
168
- </Typography>
169
- <svg className="w-3.5 h-3.5 text-text-disabled group-hover:text-primary transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24">
170
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/>
171
- </svg>
172
- </div>
173
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark text-[10px] leading-tight">
174
- {ref.description}
175
- </Typography>
176
- </div>
177
- ))}
178
- </div>
179
- </div>
180
- )}
181
-
182
- {/* Snippets */}
183
- {activeTab === "snippets" && (() => {
184
- const favorites = snippets.filter(s => s.isFavorite);
185
- const others = snippets.filter(s => !s.isFavorite);
186
-
187
- return (
188
- <div className="flex flex-col h-full">
189
- <div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
190
- <Typography variant="caption" className="font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark">Snippets</Typography>
191
- </div>
192
- <div className="flex-grow overflow-y-auto p-2 space-y-2 no-scrollbar">
193
- {snippets.length === 0 ? (
194
- <div className="p-4 text-center">
195
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark">
196
- No saved snippets yet. Use the save button to store reusable code.
197
- </Typography>
198
- </div>
199
- ) : (
200
- <>
201
- {favorites.length > 0 && (
202
- <div className="mb-3">
203
- <Typography variant="caption" className="text-[10px] font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1 flex items-center">
204
- <svg className="w-3 h-3 mr-1 text-amber-500" fill="currentColor" viewBox="0 0 20 20"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"/></svg>
205
- Favorites
206
- </Typography>
207
- <div className="space-y-2">
208
- {favorites.map(snippet => (
209
- <SnippetItem key={snippet.id} snippet={snippet} onSelect={onSelectSnippet} onDelete={onDeleteSnippet}/>
210
- ))}
211
- </div>
212
- </div>
213
- )}
214
- {others.length > 0 && (
215
- <div>
216
- {favorites.length > 0 && (
217
- <Typography variant="caption" className="text-[10px] font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark mb-2 px-1">
218
- Others
219
- </Typography>
220
- )}
221
- <div className="space-y-2">
222
- {others.map(snippet => (
223
- <SnippetItem key={snippet.id} snippet={snippet} onSelect={onSelectSnippet} onDelete={onDeleteSnippet}/>
224
- ))}
225
- </div>
226
- </div>
227
- )}
228
- </>
229
- )}
230
- </div>
231
- </div>
232
- );
233
- })()}
234
-
235
- {/* History */}
236
- {activeTab === "history" && (
237
- <div className="flex flex-col h-full">
238
- <div className={cls("flex items-center justify-between px-3 py-2 border-b bg-surface-50 dark:bg-surface-900 min-h-[48px]", defaultBorderMixin)}>
239
- <Typography variant="caption" className="font-semibold uppercase tracking-wider text-text-disabled dark:text-text-disabled-dark">History</Typography>
240
- </div>
241
- <div className="flex-grow overflow-y-auto p-1 space-y-1 no-scrollbar">
242
- {history.length === 0 ? (
243
- <div className="p-4 text-center">
244
- <Typography variant="caption" className="text-text-disabled dark:text-text-disabled-dark">
245
- No execution history yet
246
- </Typography>
247
- </div>
248
- ) : (
249
- [...history].reverse().map((code, i) => (
250
- <div
251
- key={i}
252
- className="p-2 py-1.5 rounded hover:bg-surface-100 dark:hover:bg-surface-900 cursor-pointer group transition-colors flex items-start"
253
- onClick={() => onSelectSnippet(code)}
254
- >
255
- <svg 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" fill="none" stroke="currentColor" viewBox="0 0 24 24">
256
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
257
- </svg>
258
- <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark group-hover:text-text-primary dark:group-hover:text-text-primary-dark text-[11px] line-clamp-2 leading-tight flex-grow font-mono">
259
- {code.length > 120 ? code.substring(0, 120) + "…" : code}
260
- </Typography>
261
- </div>
262
- ))
263
- )}
264
- </div>
265
- </div>
266
- )}
267
- </div>
268
- </div>
269
- );
270
- };
271
-
272
- // ─── Sub-components ──────────────────────────────────────────────────
273
-
274
- function CollectionItem({ collection, onInsertCode }: { collection: CollectionInfo; onInsertCode: (code: string) => void }) {
275
- const [expanded, setExpanded] = useState(false);
276
-
277
- return (
278
- <div>
279
- <div
280
- className="flex items-center p-1 cursor-pointer hover:bg-surface-100 dark:hover:bg-surface-900 rounded transition-colors group relative"
281
- onClick={() => setExpanded(!expanded)}
282
- >
283
- <svg
284
- className={cls("w-3 h-3 mr-1 transition-transform", expanded && "rotate-90")}
285
- fill="currentColor" viewBox="0 0 20 20"
286
- >
287
- <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"/>
288
- </svg>
289
- <svg className="w-3.5 h-3.5 mr-1 shrink-0 text-text-disabled dark:text-text-disabled-dark" fill="none" stroke="currentColor" viewBox="0 0 24 24">
290
- <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4"/>
291
- </svg>
292
- <Typography variant="body2" className="text-text-secondary dark:text-text-secondary-dark text-xs truncate flex-1 min-w-0">
293
- {collection.slug}
294
- </Typography>
295
- <Tooltip title="Insert find() snippet">
296
- <IconButton
297
- size="smallest"
298
- className="opacity-0 group-hover:opacity-100 text-text-disabled hover:text-primary transition-all"
299
- onClick={(e) => {
300
- e.stopPropagation();
301
- onInsertCode(`const result = await client.data.${collection.slug}.find({ limit: 20 });\nreturn result;`);
302
- }}
303
- >
304
- <PlusIcon size={iconSize.smallest}/>
305
- </IconButton>
306
- </Tooltip>
307
- </div>
308
- {expanded && collection.properties.length > 0 && (
309
- <div className={cls("ml-5 mt-1 space-y-0.5 border-l", defaultBorderMixin)}>
310
- {collection.properties.map(prop => (
311
- <div
312
- key={prop}
313
- 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]"
314
- onClick={() => onInsertCode(`"${prop}"`)}
315
- >
316
- <svg className="w-3 h-3 mr-1.5 text-text-disabled dark:text-text-disabled-dark shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M9 4.5v15m6-15v15m-10.5-1.5h15c.621 0 1.125-.504 1.125-1.125V5.625c0-.621-.504-1.125-1.125-1.125h-15c-.621 0-1.125.504-1.125 1.125v12.75c0 .621.504 1.125 1.125 1.125Z"/></svg>
317
- <Typography variant="caption" className="text-text-primary dark:text-text-primary-dark text-[11px] truncate flex-grow font-mono">{prop}</Typography>
318
- </div>
319
- ))}
320
- </div>
321
- )}
322
- </div>
323
- );
324
- }
325
-
326
- function SnippetItem({ snippet, onSelect, onDelete }: { snippet: JSSnippet; onSelect: (code: string) => void; onDelete: (id: string) => void }) {
327
- return (
328
- <div
329
- className={cls("group p-2 rounded border hover:border-surface-400 dark:hover:border-surface-600 bg-white dark:bg-surface-900 transition-all cursor-pointer relative", defaultBorderMixin)}
330
- onClick={() => onSelect(snippet.code)}
331
- >
332
- <Typography variant="body2" className="text-text-primary dark:text-text-primary-dark font-medium text-[13px] truncate pr-6">
333
- {snippet.name}
334
- </Typography>
335
- <Typography variant="caption" className="text-text-secondary dark:text-text-secondary-dark text-[10px] block mt-1 truncate font-mono">
336
- {snippet.code}
337
- </Typography>
338
- <IconButton
339
- size="smallest"
340
- className="absolute right-1 top-1 opacity-0 group-hover:opacity-100 text-text-disabled hover:text-red-500 transition-opacity"
341
- onClick={(e) => {
342
- e.stopPropagation();
343
- onDelete(snippet.id);
344
- }}
345
- >
346
- <Trash2Icon size={iconSize.smallest}/>
347
- </IconButton>
348
- </div>
349
- );
350
- }