@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.
- package/LICENSE +0 -1
- package/README.md +12 -4
- package/dist/{ApiKeysView-DiCurTEU.js → ApiKeysView-EIvegrUk.js} +54 -39
- package/dist/ApiKeysView-EIvegrUk.js.map +1 -0
- package/dist/{BackupsView-DNS6LdVg.js → BackupsView-DVkKzaPR.js} +29 -31
- package/dist/BackupsView-DVkKzaPR.js.map +1 -0
- package/dist/{BranchesView-CM3B-ER3.js → BranchesView-Df8hQ8wE.js} +85 -24
- package/dist/BranchesView-Df8hQ8wE.js.map +1 -0
- package/dist/{CronJobsView-BHtJZeJZ.js → CronJobsView-CLOGjb-L.js} +32 -27
- package/dist/CronJobsView-CLOGjb-L.js.map +1 -0
- package/dist/{RLSEditor-CBX4uD66.js → RLSEditor-Sa1D34ny.js} +724 -615
- package/dist/RLSEditor-Sa1D34ny.js.map +1 -0
- package/dist/{SQLEditor-ByecwX8B.js → SQLEditor-BfNA8xM3.js} +9 -4
- package/dist/{SQLEditor-ByecwX8B.js.map → SQLEditor-BfNA8xM3.js.map} +1 -1
- package/dist/{SchemaVisualizer-D6tMZgeC.js → SchemaVisualizer-DTDMYL6o.js} +84 -110
- package/dist/SchemaVisualizer-DTDMYL6o.js.map +1 -0
- package/dist/{StorageView-BGDeC5GI.js → StorageView-Ed9Q_VlB.js} +5 -32
- package/dist/StorageView-Ed9Q_VlB.js.map +1 -0
- package/dist/components/RLSEditor/saveRules.d.ts +38 -0
- package/dist/components/RebaseStudio.d.ts +1 -1
- package/dist/components/SchemaVisualizer/schema-visualizer.utils.d.ts +5 -2
- package/dist/components/SchemaVisualizer/useSchemaGraph.d.ts +0 -4
- package/dist/components/load-failure-view.d.ts +17 -0
- package/dist/components/load-failure.d.ts +47 -0
- package/dist/index.es.js +56 -36
- package/dist/index.es.js.map +1 -1
- package/dist/load-failure-C6OMJSGC.js +31 -0
- package/dist/load-failure-C6OMJSGC.js.map +1 -0
- package/dist/load-failure-view-CBTB_h-h.js +48 -0
- package/dist/load-failure-view-CBTB_h-h.js.map +1 -0
- package/package.json +36 -21
- package/dist/ApiKeysView-DiCurTEU.js.map +0 -1
- package/dist/BackupsView-DNS6LdVg.js.map +0 -1
- package/dist/BranchesView-CM3B-ER3.js.map +0 -1
- package/dist/CronJobsView-BHtJZeJZ.js.map +0 -1
- package/dist/RLSEditor-CBX4uD66.js.map +0 -1
- package/dist/SchemaVisualizer-D6tMZgeC.js.map +0 -1
- package/dist/StorageView-BGDeC5GI.js.map +0 -1
- package/dist/components/StorageView/storage-failure.d.ts +0 -34
- package/src/components/ApiExplorer/ApiExplorer.tsx +0 -299
- package/src/components/ApiExplorer/EndpointDetail.tsx +0 -279
- package/src/components/ApiExplorer/TryItPanel.tsx +0 -499
- package/src/components/ApiExplorer/parseSpec.ts +0 -104
- package/src/components/ApiExplorer/types.ts +0 -84
- package/src/components/ApiKeys/ApiKeysView.tsx +0 -479
- package/src/components/ApiKeys/CreateApiKeyDialog.tsx +0 -711
- package/src/components/ApiKeys/permissions.ts +0 -145
- package/src/components/AuthSimulationSelector.tsx +0 -72
- package/src/components/Backups/BackupsView.tsx +0 -185
- package/src/components/Branches/BranchesView.tsx +0 -386
- package/src/components/CronJobs/CronJobsView.tsx +0 -413
- package/src/components/JSEditor/JSEditor.tsx +0 -1100
- package/src/components/JSEditor/JSEditorSidebar.tsx +0 -350
- package/src/components/JSEditor/JSMonacoEditor.tsx +0 -410
- package/src/components/LogsExplorer/LogsExplorer.tsx +0 -273
- package/src/components/LogsExplorer/useLogTail.ts +0 -356
- package/src/components/RLSEditor/PolicyEditor.tsx +0 -399
- package/src/components/RLSEditor/RLSEditor.tsx +0 -1128
- package/src/components/RLSEditor/index.ts +0 -1
- package/src/components/RLSEditor/policy-presets.ts +0 -176
- package/src/components/RLSEditor/saveSecurityRules.ts +0 -48
- package/src/components/RebaseStudio.tsx +0 -174
- package/src/components/SQLEditor/ExplainVisualizer.tsx +0 -128
- package/src/components/SQLEditor/MonacoEditor.tsx +0 -203
- package/src/components/SQLEditor/SQLEditor.tsx +0 -1518
- package/src/components/SQLEditor/SQLEditorSidebar.tsx +0 -173
- package/src/components/SQLEditor/SchemaBrowser.tsx +0 -168
- package/src/components/SQLEditor/sql_editor_types.ts +0 -18
- package/src/components/SchemaVisualizer/RelationEdge.tsx +0 -102
- package/src/components/SchemaVisualizer/SchemaVisualizer.tsx +0 -710
- package/src/components/SchemaVisualizer/TableNode.tsx +0 -257
- package/src/components/SchemaVisualizer/schema-visualizer.utils.ts +0 -140
- package/src/components/SchemaVisualizer/useLiveRls.ts +0 -86
- package/src/components/SchemaVisualizer/useSchemaGraph.ts +0 -426
- package/src/components/StorageView/StorageView.tsx +0 -1588
- package/src/components/StorageView/storage-failure.ts +0 -63
- package/src/components/StudioHomePage.tsx +0 -430
- package/src/index.ts +0 -31
- package/src/utils/parseSpec.test.ts +0 -309
- package/src/utils/sql_utils.test.ts +0 -265
- package/src/utils/sql_utils.ts +0 -291
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Why a storage listing failed — and whether it is the platform's fault.
|
|
3
|
-
*
|
|
4
|
-
* ## Why this is a function
|
|
5
|
-
*
|
|
6
|
-
* The pane rendered one view for every failure: a red card titled **"Error
|
|
7
|
-
* loading storage"** with whatever string came back underneath.
|
|
8
|
-
*
|
|
9
|
-
* On 2026-08-27 a customer opened the Files tab of a healthy project and read
|
|
10
|
-
* "Error loading storage — Not authorized for this object". Nothing was broken.
|
|
11
|
-
* That project's own `storageAuthorize` hook refuses an unscoped listing on
|
|
12
|
-
* purpose — its comment says a `list` that is not scoped to a team "is precisely
|
|
13
|
-
* the enumeration this exists to stop" — and the Files tab always opens at the
|
|
14
|
-
* bucket root. The console took a policy working exactly as written and
|
|
15
|
-
* presented it as a fault in the platform, which sends the reader hunting for a
|
|
16
|
-
* problem that does not exist.
|
|
17
|
-
*
|
|
18
|
-
* A refusal and a failure need different words, a different colour, and a
|
|
19
|
-
* different next step: one is "your rule said no", the other is "we could not
|
|
20
|
-
* ask". Retry is an affordance for the second and noise on the first.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
export type StorageFailureKind =
|
|
24
|
-
/** The project's own authorize hook said no. Not an error. */
|
|
25
|
-
| "denied"
|
|
26
|
-
/** We could not reach or read the store. */
|
|
27
|
-
| "unavailable";
|
|
28
|
-
|
|
29
|
-
export interface StorageFailure {
|
|
30
|
-
kind: StorageFailureKind;
|
|
31
|
-
/** The message as the server gave it, never re-worded. */
|
|
32
|
-
detail: string;
|
|
33
|
-
/** Offering retry against a standing policy teaches people the button does nothing. */
|
|
34
|
-
retryable: boolean;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** HTTP status carried by whatever the SDK threw, if it carries one. */
|
|
38
|
-
function statusOf(err: unknown): number | null {
|
|
39
|
-
if (typeof err !== "object" || err === null) return null;
|
|
40
|
-
for (const key of ["status", "statusCode", "code"]) {
|
|
41
|
-
const v = (err as Record<string, unknown>)[key];
|
|
42
|
-
if (typeof v === "number" && v >= 100 && v < 600) return v;
|
|
43
|
-
}
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function classifyStorageFailure(err: unknown): StorageFailure {
|
|
48
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
49
|
-
const status = statusOf(err);
|
|
50
|
-
|
|
51
|
-
// 403 is the hook's answer; 401 means the caller is not signed in as anyone
|
|
52
|
-
// the hook can evaluate, which is the same conversation from the other end.
|
|
53
|
-
// The message match is the fallback, because an SDK that flattens an error
|
|
54
|
-
// to a string is exactly how this reached the screen unclassified.
|
|
55
|
-
const denied =
|
|
56
|
-
status === 403 ||
|
|
57
|
-
status === 401 ||
|
|
58
|
-
/\bnot authori[sz]ed\b|\bforbidden\b|\bpermission denied\b/i.test(detail);
|
|
59
|
-
|
|
60
|
-
return denied
|
|
61
|
-
? { kind: "denied", detail, retryable: false }
|
|
62
|
-
: { kind: "unavailable", detail, retryable: true };
|
|
63
|
-
}
|
|
@@ -1,430 +0,0 @@
|
|
|
1
|
-
import type { AppView, HomePageSection, PluginGenericProps } from "@rebasepro/cms-types";
|
|
2
|
-
import React, { useEffect, useMemo, useState } from "react";
|
|
3
|
-
import { Card, cls, Container, ExpandablePanel, Typography } from "@rebasepro/ui";
|
|
4
|
-
import { IconForView, useRebaseContext, useRebaseRegistry, useRestoreScroll, useSlot } from "@rebasepro/app";
|
|
5
|
-
import { useNavigate } from "react-router";
|
|
6
|
-
import { useStudioBreadcrumbs, SchemaDriftBanner } from "@rebasepro/app";
|
|
7
|
-
|
|
8
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
9
|
-
Studio tool sections, derived from the registered Studio views
|
|
10
|
-
═══════════════════════════════════════════════════════════════ */
|
|
11
|
-
|
|
12
|
-
interface StudioTool {
|
|
13
|
-
path: string;
|
|
14
|
-
name: string;
|
|
15
|
-
description: string;
|
|
16
|
-
icon?: string | React.ReactNode;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface StudioSection {
|
|
20
|
-
label: string;
|
|
21
|
-
tools: StudioTool[];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** The view metadata the cards need — not the rendered `view` itself. */
|
|
25
|
-
type StudioViewMeta = Pick<AppView, "slug" | "name" | "group" | "description" | "icon" | "hideFromNavigation">;
|
|
26
|
-
|
|
27
|
-
/** Group order for the home page; unknown groups are appended in view order. */
|
|
28
|
-
const GROUP_ORDER = ["Database", "Compute", "Storage", "API", "Access Control"];
|
|
29
|
-
|
|
30
|
-
const UNGROUPED_LABEL = "Tools";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Build the home page sections from the Studio views that are actually
|
|
34
|
-
* registered, so a card exists if and only if its route does. A hand-written
|
|
35
|
-
* list drifts: it used to advertise Users and Roles pages that 404, while
|
|
36
|
-
* omitting the Backups view that does exist.
|
|
37
|
-
*/
|
|
38
|
-
function buildSections(views: StudioViewMeta[]): StudioSection[] {
|
|
39
|
-
const byGroup = new Map<string, StudioTool[]>();
|
|
40
|
-
|
|
41
|
-
for (const view of views) {
|
|
42
|
-
if (view.hideFromNavigation) continue;
|
|
43
|
-
const label = view.group ?? UNGROUPED_LABEL;
|
|
44
|
-
const tools = byGroup.get(label) ?? [];
|
|
45
|
-
tools.push({
|
|
46
|
-
path: `/${view.slug}`,
|
|
47
|
-
name: view.name,
|
|
48
|
-
description: view.description ?? "",
|
|
49
|
-
icon: view.icon
|
|
50
|
-
});
|
|
51
|
-
byGroup.set(label, tools);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const ordered = [
|
|
55
|
-
...GROUP_ORDER.filter(g => byGroup.has(g)),
|
|
56
|
-
...[...byGroup.keys()].filter(g => !GROUP_ORDER.includes(g))
|
|
57
|
-
];
|
|
58
|
-
|
|
59
|
-
return ordered.map(label => ({ label,
|
|
60
|
-
tools: byGroup.get(label)! }));
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/* ═══════════════════════════════════════════════════════════════ */
|
|
64
|
-
|
|
65
|
-
const COLLAPSED_STORAGE_KEY = "rebase-studio-home-collapsed";
|
|
66
|
-
|
|
67
|
-
function useStudioCollapsedGroups(groupNames: string[]) {
|
|
68
|
-
const [collapsed, setCollapsed] = useState<Set<string>>(() => {
|
|
69
|
-
try {
|
|
70
|
-
const stored = localStorage.getItem(COLLAPSED_STORAGE_KEY);
|
|
71
|
-
return stored ? new Set(JSON.parse(stored)) : new Set<string>();
|
|
72
|
-
} catch {
|
|
73
|
-
return new Set<string>();
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const isGroupCollapsed = (name: string) => collapsed.has(name);
|
|
78
|
-
|
|
79
|
-
const toggleGroupCollapsed = (name: string) => {
|
|
80
|
-
setCollapsed(prev => {
|
|
81
|
-
const next = new Set(prev);
|
|
82
|
-
if (next.has(name)) {
|
|
83
|
-
next.delete(name);
|
|
84
|
-
} else {
|
|
85
|
-
next.add(name);
|
|
86
|
-
}
|
|
87
|
-
try {
|
|
88
|
-
localStorage.setItem(COLLAPSED_STORAGE_KEY, JSON.stringify([...next]));
|
|
89
|
-
} catch { /* noop */ }
|
|
90
|
-
return next;
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
return { isGroupCollapsed, toggleGroupCollapsed };
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function StudioHomePage({
|
|
98
|
-
additionalActions,
|
|
99
|
-
additionalChildrenStart,
|
|
100
|
-
additionalChildrenEnd,
|
|
101
|
-
sections,
|
|
102
|
-
hiddenGroups
|
|
103
|
-
}: {
|
|
104
|
-
additionalActions?: React.ReactNode;
|
|
105
|
-
additionalChildrenStart?: React.ReactNode;
|
|
106
|
-
additionalChildrenEnd?: React.ReactNode;
|
|
107
|
-
sections?: HomePageSection[];
|
|
108
|
-
hiddenGroups?: string[];
|
|
109
|
-
}) {
|
|
110
|
-
const context = useRebaseContext();
|
|
111
|
-
const breadcrumbs = useStudioBreadcrumbs();
|
|
112
|
-
const navigate = useNavigate();
|
|
113
|
-
const registry = useRebaseRegistry();
|
|
114
|
-
|
|
115
|
-
useEffect(() => {
|
|
116
|
-
breadcrumbs.set({ breadcrumbs: [] });
|
|
117
|
-
}, [breadcrumbs.set]);
|
|
118
|
-
|
|
119
|
-
const { containerRef } = useRestoreScroll();
|
|
120
|
-
|
|
121
|
-
const sectionProps: PluginGenericProps = { context };
|
|
122
|
-
|
|
123
|
-
const pluginActions = useSlot("home.actions", sectionProps);
|
|
124
|
-
|
|
125
|
-
// The collection editor ("schema") is not part of `devViews` — RebaseNavigation
|
|
126
|
-
// injects it when the admin enables a collection editor. Mirror that condition so
|
|
127
|
-
// the card tracks the route.
|
|
128
|
-
const schemaEnabled = Boolean(registry.studioConfig && registry.cmsConfig?.collectionEditor);
|
|
129
|
-
|
|
130
|
-
const filteredSections = useMemo(() => {
|
|
131
|
-
const views: StudioViewMeta[] = [];
|
|
132
|
-
if (schemaEnabled) {
|
|
133
|
-
views.push({
|
|
134
|
-
slug: "schema",
|
|
135
|
-
name: "Collections",
|
|
136
|
-
group: "Database",
|
|
137
|
-
icon: "LayoutList",
|
|
138
|
-
description: "Define and manage your data model and collection schemas"
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
views.push(...(registry.studioConfig?.devViews ?? []));
|
|
142
|
-
return buildSections(views).filter(s => !hiddenGroups?.includes(s.label));
|
|
143
|
-
}, [registry.studioConfig?.devViews, schemaEnabled, hiddenGroups]);
|
|
144
|
-
|
|
145
|
-
const groupNames = useMemo(
|
|
146
|
-
() => filteredSections.map(s => s.label),
|
|
147
|
-
[filteredSections]
|
|
148
|
-
);
|
|
149
|
-
|
|
150
|
-
const { isGroupCollapsed, toggleGroupCollapsed } = useStudioCollapsedGroups(groupNames);
|
|
151
|
-
|
|
152
|
-
return (
|
|
153
|
-
<div ref={containerRef} className="py-2 overflow-auto h-full w-full bg-surface-50 dark:bg-surface-800">
|
|
154
|
-
<Container maxWidth="6xl">
|
|
155
|
-
<div className="mb-4 flex flex-col gap-2">
|
|
156
|
-
<SchemaDriftBanner />
|
|
157
|
-
</div>
|
|
158
|
-
|
|
159
|
-
{(additionalActions || pluginActions) && (
|
|
160
|
-
<div className="w-full sticky py-4 transition-all duration-400 ease-in-out top-0 z-10 flex flex-row gap-4 justify-end">
|
|
161
|
-
{additionalActions}
|
|
162
|
-
{pluginActions}
|
|
163
|
-
</div>
|
|
164
|
-
)}
|
|
165
|
-
|
|
166
|
-
{additionalChildrenStart}
|
|
167
|
-
|
|
168
|
-
{/* ── Tool sections ── */}
|
|
169
|
-
{filteredSections.map((section) => {
|
|
170
|
-
const sectionCollapsed = isGroupCollapsed(section.label);
|
|
171
|
-
|
|
172
|
-
return (
|
|
173
|
-
<div key={section.label} className="my-10">
|
|
174
|
-
<ExpandablePanel
|
|
175
|
-
invisible
|
|
176
|
-
expanded={!sectionCollapsed}
|
|
177
|
-
onExpandedChange={(open) => {
|
|
178
|
-
if (open !== !sectionCollapsed) {
|
|
179
|
-
toggleGroupCollapsed(section.label);
|
|
180
|
-
}
|
|
181
|
-
}}
|
|
182
|
-
className="mt-6"
|
|
183
|
-
titleClassName={cls(
|
|
184
|
-
"min-h-0 p-0 border-none",
|
|
185
|
-
"rounded flex items-center justify-between w-full",
|
|
186
|
-
"hover:bg-transparent",
|
|
187
|
-
"cursor-pointer select-none",
|
|
188
|
-
sectionCollapsed && "bg-surface-100 dark:bg-surface-900/50"
|
|
189
|
-
)}
|
|
190
|
-
innerClassName="mt-4 pt-0"
|
|
191
|
-
title={
|
|
192
|
-
<Typography
|
|
193
|
-
variant="caption"
|
|
194
|
-
component="h2"
|
|
195
|
-
color="secondary"
|
|
196
|
-
className={cls(
|
|
197
|
-
"px-4 py-1 rounded",
|
|
198
|
-
"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70"
|
|
199
|
-
)}
|
|
200
|
-
>
|
|
201
|
-
{section.label}
|
|
202
|
-
</Typography>
|
|
203
|
-
}
|
|
204
|
-
>
|
|
205
|
-
<div className="mt-4 pt-0">
|
|
206
|
-
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
|
207
|
-
{section.tools.map((tool) => (
|
|
208
|
-
<Card
|
|
209
|
-
key={tool.path}
|
|
210
|
-
onClick={() => {
|
|
211
|
-
navigate(tool.path);
|
|
212
|
-
context.analyticsController?.onAnalyticsEvent?.(
|
|
213
|
-
"home_navigate_to_view",
|
|
214
|
-
{ path: tool.path }
|
|
215
|
-
);
|
|
216
|
-
}}
|
|
217
|
-
className={cls(
|
|
218
|
-
"group h-full p-4 cursor-pointer transition-colors duration-150 ease-in-out",
|
|
219
|
-
"hover:bg-primary/5 dark:hover:bg-primary/5"
|
|
220
|
-
)}
|
|
221
|
-
>
|
|
222
|
-
<div className="flex flex-col h-full">
|
|
223
|
-
{/* Header: icon + title */}
|
|
224
|
-
<div className="flex items-center w-full justify-between mb-1">
|
|
225
|
-
<div className="flex items-center gap-3">
|
|
226
|
-
<div className="flex items-center justify-center w-5 h-5 text-surface-400 dark:text-surface-500 transition-colors duration-150 group-hover:text-primary dark:group-hover:text-primary">
|
|
227
|
-
<IconForView
|
|
228
|
-
collectionOrView={{ slug: tool.path, name: tool.name, icon: tool.icon }}
|
|
229
|
-
size="small"
|
|
230
|
-
/>
|
|
231
|
-
</div>
|
|
232
|
-
<Typography variant="subtitle1" component="h2">
|
|
233
|
-
{tool.name}
|
|
234
|
-
</Typography>
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
|
|
238
|
-
{/* Description indented to align with title */}
|
|
239
|
-
<div className="pl-8">
|
|
240
|
-
{tool.description && (
|
|
241
|
-
<Typography variant="caption" color="secondary" component="div">
|
|
242
|
-
{tool.description}
|
|
243
|
-
</Typography>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
246
|
-
|
|
247
|
-
{/* Spacer */}
|
|
248
|
-
<div className="grow"/>
|
|
249
|
-
</div>
|
|
250
|
-
</Card>
|
|
251
|
-
))}
|
|
252
|
-
</div>
|
|
253
|
-
</div>
|
|
254
|
-
</ExpandablePanel>
|
|
255
|
-
</div>
|
|
256
|
-
);
|
|
257
|
-
})}
|
|
258
|
-
|
|
259
|
-
{/* ── SDK Quick Start ── */}
|
|
260
|
-
<div className="mt-10 mb-6">
|
|
261
|
-
<div className="flex items-center mb-1">
|
|
262
|
-
<Typography
|
|
263
|
-
variant="caption"
|
|
264
|
-
component="h2"
|
|
265
|
-
color="secondary"
|
|
266
|
-
className={cls(
|
|
267
|
-
"px-4 py-1 rounded",
|
|
268
|
-
"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70"
|
|
269
|
-
)}
|
|
270
|
-
>
|
|
271
|
-
Quick Start
|
|
272
|
-
</Typography>
|
|
273
|
-
</div>
|
|
274
|
-
|
|
275
|
-
<Typography variant="body2" color="secondary" className="mb-4 max-w-2xl">
|
|
276
|
-
Generate a fully-typed SDK from your collections with{" "}
|
|
277
|
-
<code className="text-emerald-400 font-mono text-xs bg-emerald-400/10 px-1.5 py-0.5 rounded">
|
|
278
|
-
npx rebase generate-sdk
|
|
279
|
-
</code>
|
|
280
|
-
{" "}and start querying your data with full TypeScript autocompletion.
|
|
281
|
-
</Typography>
|
|
282
|
-
|
|
283
|
-
<div className="rounded-lg border border-surface-200/40 dark:border-surface-700/40 bg-white dark:bg-surface-950 overflow-hidden">
|
|
284
|
-
{/* Title bar */}
|
|
285
|
-
<div className="flex items-center justify-between px-4 py-2.5 border-b border-surface-200/40 dark:border-surface-700/40 bg-surface-50 dark:bg-surface-900/80">
|
|
286
|
-
<div className="flex items-center gap-2.5">
|
|
287
|
-
<div className="flex gap-1.5">
|
|
288
|
-
<span className="w-2.5 h-2.5 rounded-full bg-red-400/60"/>
|
|
289
|
-
<span className="w-2.5 h-2.5 rounded-full bg-amber-400/60"/>
|
|
290
|
-
<span className="w-2.5 h-2.5 rounded-full bg-emerald-400/60"/>
|
|
291
|
-
</div>
|
|
292
|
-
<span className="text-xs font-mono text-surface-400 dark:text-surface-500 ml-1">
|
|
293
|
-
app.ts
|
|
294
|
-
</span>
|
|
295
|
-
</div>
|
|
296
|
-
<span className="text-xs font-mono text-surface-400 dark:text-surface-500">
|
|
297
|
-
TypeScript
|
|
298
|
-
</span>
|
|
299
|
-
</div>
|
|
300
|
-
|
|
301
|
-
{/* Syntax-highlighted code */}
|
|
302
|
-
<div className="px-5 py-4 overflow-x-auto text-[13px] leading-6 font-mono">
|
|
303
|
-
<SyntaxHighlightedSnippet/>
|
|
304
|
-
</div>
|
|
305
|
-
</div>
|
|
306
|
-
</div>
|
|
307
|
-
|
|
308
|
-
{/* ── Extra sections from props ── */}
|
|
309
|
-
{sections?.map((s) => (
|
|
310
|
-
<div key={s.key} className="my-10">
|
|
311
|
-
<Typography
|
|
312
|
-
variant="caption"
|
|
313
|
-
component="h2"
|
|
314
|
-
color="secondary"
|
|
315
|
-
className={cls(
|
|
316
|
-
"px-4 py-1 rounded",
|
|
317
|
-
"font-medium text-[10px] uppercase tracking-[0.08em] text-primary/50 dark:text-primary/70"
|
|
318
|
-
)}
|
|
319
|
-
>
|
|
320
|
-
{s.title}
|
|
321
|
-
</Typography>
|
|
322
|
-
<div className="mt-4">{s.children}</div>
|
|
323
|
-
</div>
|
|
324
|
-
))}
|
|
325
|
-
|
|
326
|
-
{additionalChildrenEnd}
|
|
327
|
-
</Container>
|
|
328
|
-
</div>
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/* ═══════════════════════════════════════════════════════════════
|
|
333
|
-
Hand-crafted syntax-highlighted code snippet.
|
|
334
|
-
Uses inline spans with Tailwind color classes to avoid
|
|
335
|
-
pulling in a syntax highlighting library.
|
|
336
|
-
═══════════════════════════════════════════════════════════════ */
|
|
337
|
-
|
|
338
|
-
function SyntaxHighlightedSnippet() {
|
|
339
|
-
const kw = "text-violet-600 dark:text-violet-400"; // keywords
|
|
340
|
-
const str = "text-emerald-600 dark:text-emerald-400"; // strings
|
|
341
|
-
const typ = "text-amber-600 dark:text-amber-300"; // types
|
|
342
|
-
const fn = "text-blue-600 dark:text-blue-400"; // functions
|
|
343
|
-
const cm = "text-surface-500 dark:text-surface-400 italic"; // comments
|
|
344
|
-
const op = "text-surface-500 dark:text-surface-400"; // operators / punctuation
|
|
345
|
-
const tx = "text-surface-950 dark:text-surface-200"; // plain text
|
|
346
|
-
|
|
347
|
-
return (
|
|
348
|
-
<pre className="m-0 whitespace-pre">
|
|
349
|
-
<span className={kw}>import</span>
|
|
350
|
-
<span className={tx}>{" { "}</span>
|
|
351
|
-
<span className={fn}>createRebaseClient</span>
|
|
352
|
-
<span className={tx}>{" } "}</span>
|
|
353
|
-
<span className={kw}>from</span>
|
|
354
|
-
<span className={tx}> </span>
|
|
355
|
-
<span className={str}>'@rebasepro/client'</span>
|
|
356
|
-
<span className={op}>;</span>
|
|
357
|
-
{"\n"}
|
|
358
|
-
|
|
359
|
-
<span className={kw}>import</span>
|
|
360
|
-
<span className={tx}> </span>
|
|
361
|
-
<span className={kw}>type</span>
|
|
362
|
-
<span className={tx}>{" { "}</span>
|
|
363
|
-
<span className={typ}>Database</span>
|
|
364
|
-
<span className={tx}>{" } "}</span>
|
|
365
|
-
<span className={kw}>from</span>
|
|
366
|
-
<span className={tx}> </span>
|
|
367
|
-
<span className={str}>'./database.types'</span>
|
|
368
|
-
<span className={op}>;</span>
|
|
369
|
-
{"\n\n"}
|
|
370
|
-
|
|
371
|
-
<span className={kw}>const</span>
|
|
372
|
-
<span className={tx}> rebase </span>
|
|
373
|
-
<span className={op}>= </span>
|
|
374
|
-
<span className={fn}>createRebaseClient</span>
|
|
375
|
-
<span className={op}>{"<"}</span>
|
|
376
|
-
<span className={typ}>Database</span>
|
|
377
|
-
<span className={op}>{">("}</span>
|
|
378
|
-
<span className={tx}>{"{"}</span>
|
|
379
|
-
{"\n"}
|
|
380
|
-
<span className={tx}>{" baseUrl"}</span>
|
|
381
|
-
<span className={op}>: </span>
|
|
382
|
-
<span className={str}>'http://localhost:3001'</span>
|
|
383
|
-
<span className={op}>,</span>
|
|
384
|
-
{"\n"}
|
|
385
|
-
<span className={tx}>{"}"}</span>
|
|
386
|
-
<span className={op}>);</span>
|
|
387
|
-
{"\n\n"}
|
|
388
|
-
|
|
389
|
-
<span className={cm}>{"// Fully typed — autocompletion for tables and columns"}</span>
|
|
390
|
-
{"\n"}
|
|
391
|
-
<span className={kw}>const</span>
|
|
392
|
-
<span className={tx}>{" { "}</span>
|
|
393
|
-
<span className={tx}>data</span>
|
|
394
|
-
<span className={op}>: </span>
|
|
395
|
-
<span className={tx}>users</span>
|
|
396
|
-
<span className={tx}>{" } "}</span>
|
|
397
|
-
<span className={op}>= </span>
|
|
398
|
-
<span className={kw}>await</span>
|
|
399
|
-
<span className={tx}> rebase</span>
|
|
400
|
-
<span className={op}>.</span>
|
|
401
|
-
<span className={tx}>data</span>
|
|
402
|
-
<span className={op}>.</span>
|
|
403
|
-
<span className={tx}>users</span>
|
|
404
|
-
<span className={op}>.</span>
|
|
405
|
-
<span className={fn}>find</span>
|
|
406
|
-
<span className={op}>();</span>
|
|
407
|
-
{"\n"}
|
|
408
|
-
|
|
409
|
-
<span className={kw}>const</span>
|
|
410
|
-
<span className={tx}>{" { "}</span>
|
|
411
|
-
<span className={tx}>data</span>
|
|
412
|
-
<span className={op}>: </span>
|
|
413
|
-
<span className={tx}>posts</span>
|
|
414
|
-
<span className={tx}>{" } "}</span>
|
|
415
|
-
<span className={op}>= </span>
|
|
416
|
-
<span className={kw}>await</span>
|
|
417
|
-
<span className={tx}> rebase</span>
|
|
418
|
-
<span className={op}>.</span>
|
|
419
|
-
<span className={tx}>data</span>
|
|
420
|
-
<span className={op}>.</span>
|
|
421
|
-
<span className={fn}>collection</span>
|
|
422
|
-
<span className={op}>(</span>
|
|
423
|
-
<span className={str}>'posts'</span>
|
|
424
|
-
<span className={op}>)</span>
|
|
425
|
-
<span className={op}>.</span>
|
|
426
|
-
<span className={fn}>find</span>
|
|
427
|
-
<span className={op}>();</span>
|
|
428
|
-
</pre>
|
|
429
|
-
);
|
|
430
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// ─── Bridge ─────────────────────────────────────────────────────────
|
|
2
|
-
// Re-export the Studio Bridge from @rebasepro/app.
|
|
3
|
-
// The bridge lives in core so both studio and admin can access it
|
|
4
|
-
// without circular dependencies.
|
|
5
|
-
export {
|
|
6
|
-
StudioBridgeProvider,
|
|
7
|
-
StudioBridgeContext,
|
|
8
|
-
useStudioCollectionRegistry,
|
|
9
|
-
useStudioSidePanelController,
|
|
10
|
-
useStudioUrlController,
|
|
11
|
-
useStudioNavigationState,
|
|
12
|
-
useStudioBreadcrumbs
|
|
13
|
-
} from "@rebasepro/app";
|
|
14
|
-
export type {
|
|
15
|
-
StudioBridge,
|
|
16
|
-
BreadcrumbEntry,
|
|
17
|
-
BreadcrumbsController
|
|
18
|
-
} from "@rebasepro/app";
|
|
19
|
-
|
|
20
|
-
// ─── Studio Entry Point ─────────────────────────────────────────────
|
|
21
|
-
// Only export the lightweight orchestrator & home page.
|
|
22
|
-
// Individual tools (SQLEditor, JSEditor, RLSEditor, StorageView, etc.)
|
|
23
|
-
// are lazy-loaded by RebaseStudio.tsx — DO NOT re-export them here
|
|
24
|
-
// or they'll be pulled into the main bundle, defeating code splitting.
|
|
25
|
-
export * from "./components/RebaseStudio";
|
|
26
|
-
export * from "./components/StudioHomePage";
|
|
27
|
-
|
|
28
|
-
// ─── Direct tool imports (for advanced usage) ───────────────────────
|
|
29
|
-
// Consumers that need direct access to a tool should use deep imports:
|
|
30
|
-
// import { SQLEditor } from "@rebasepro/studio/components/SQLEditor/SQLEditor";
|
|
31
|
-
// This avoids pulling all tools into their bundle.
|