@nextsparkjs/core 0.1.0-beta.177 → 0.1.0-beta.178
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/dist/components/devtools/MarkdownViewer.d.ts.map +1 -1
- package/dist/components/devtools/MarkdownViewer.js +5 -0
- package/dist/components/devtools/api-explorer/ApiDocsModal.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiDocsModal.js +0 -0
- package/dist/components/devtools/api-explorer/ApiEndpointsSidebar.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiEndpointsSidebar.js +16 -5
- package/dist/components/devtools/api-explorer/ApiExplorer.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiExplorer.js +63 -21
- package/dist/components/devtools/api-explorer/ApiRequestPanel.d.ts +3 -1
- package/dist/components/devtools/api-explorer/ApiRequestPanel.d.ts.map +1 -1
- package/dist/components/devtools/api-explorer/ApiRequestPanel.js +5 -1
- package/dist/components/devtools/api-explorer/explorer-storage.d.ts +12 -0
- package/dist/components/devtools/api-explorer/explorer-storage.d.ts.map +1 -0
- package/dist/components/devtools/api-explorer/explorer-storage.js +25 -0
- package/dist/components/devtools/api-tester/AuthSelector.d.ts +4 -1
- package/dist/components/devtools/api-tester/AuthSelector.d.ts.map +1 -1
- package/dist/components/devtools/api-tester/AuthSelector.js +24 -2
- package/dist/components/docs/docs-content.d.ts.map +1 -1
- package/dist/components/docs/docs-content.js +17 -0
- package/dist/components/docs/mermaid-diagram.d.ts +11 -0
- package/dist/components/docs/mermaid-diagram.d.ts.map +1 -0
- package/dist/components/docs/mermaid-diagram.js +20 -0
- package/dist/hooks/useApiExplorerNavigation.d.ts.map +1 -1
- package/dist/hooks/useApiExplorerNavigation.js +5 -1
- package/dist/lib/api/auth/dual-auth.d.ts +14 -0
- package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
- package/dist/lib/api/auth/dual-auth.js +43 -2
- package/dist/lib/docs/mermaid.d.ts +15 -0
- package/dist/lib/docs/mermaid.d.ts.map +1 -0
- package/dist/lib/docs/mermaid.js +42 -0
- package/dist/lib/docs/parser.d.ts.map +1 -1
- package/dist/lib/docs/parser.js +2 -1
- package/dist/lib/docs/remark-mermaid.d.ts +15 -0
- package/dist/lib/docs/remark-mermaid.d.ts.map +1 -0
- package/dist/lib/docs/remark-mermaid.js +26 -0
- package/dist/styles/classes.json +3 -2
- package/dist/templates/app/(auth)/login/page.tsx +2 -0
- package/dist/templates/app/(auth)/signup/page.tsx +4 -0
- package/package.json +3 -2
- package/styles/docs.css +15 -0
- package/templates/app/(auth)/login/page.tsx +2 -0
- package/templates/app/(auth)/signup/page.tsx +4 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MarkdownViewer.d.ts","sourceRoot":"","sources":["../../../src/components/devtools/MarkdownViewer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"MarkdownViewer.d.ts","sourceRoot":"","sources":["../../../src/components/devtools/MarkdownViewer.tsx"],"names":[],"mappings":"AASA,UAAU,mBAAmB;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAsED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,mBAAmB,2CAgDzE"}
|
|
@@ -5,6 +5,7 @@ import remarkGfm from "remark-gfm";
|
|
|
5
5
|
import { useEffect, useState, memo } from "react";
|
|
6
6
|
import { codeToHtml } from "shiki";
|
|
7
7
|
import { cn } from "../../lib/utils/index.js";
|
|
8
|
+
import { MermaidDiagram } from "../docs/mermaid-diagram.js";
|
|
8
9
|
const ShikiCodeBlock = memo(function ShikiCodeBlock2({
|
|
9
10
|
code,
|
|
10
11
|
language
|
|
@@ -72,6 +73,10 @@ function MarkdownViewer({ content, className }) {
|
|
|
72
73
|
const match = /language-(\w+)/.exec(className2 || "");
|
|
73
74
|
const language = match ? match[1] : "";
|
|
74
75
|
const isInline = !match;
|
|
76
|
+
if (!isInline && language === "mermaid") {
|
|
77
|
+
const chart = String(children).replace(/\n$/, "");
|
|
78
|
+
return /* @__PURE__ */ jsx(MermaidDiagram, { chart });
|
|
79
|
+
}
|
|
75
80
|
if (!isInline && language) {
|
|
76
81
|
const code = String(children).replace(/\n$/, "");
|
|
77
82
|
return /* @__PURE__ */ jsx(ShikiCodeBlock, { code, language });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiDocsModal.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiDocsModal.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ApiDocsModal.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiDocsModal.tsx"],"names":[],"mappings":"AAgBA,UAAU,iBAAiB;IACzB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,wCAAwC;IACxC,KAAK,EAAE,MAAM,CAAA;CACd;AAED,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,iBAAiB,2CAwEjE"}
|
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiEndpointsSidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiEndpointsSidebar.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ApiEndpointsSidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiEndpointsSidebar.tsx"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAC5F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AA4CrD,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,UAAU,CAAA;CACnB;AAED,UAAU,wBAAwB;IAChC,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,CAAC,CAAA;IAC9C,gBAAgB,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACzC,gBAAgB,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,KAAK,IAAI,CAAA;IAClF,WAAW,EAAE,OAAO,CAAA;IACpB,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B;AAED,wBAAgB,mBAAmB,CAAC,EAClC,MAAM,EACN,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,gBAAgB,GACjB,EAAE,wBAAwB,2CAkQ1B"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useMemo } from "react";
|
|
3
|
+
import { useState, useMemo, useEffect } from "react";
|
|
4
4
|
import { Search, ChevronRight, ChevronDown, Code, Database, Layers, Puzzle, PanelLeftClose, PanelLeft, Folder, FolderOpen } from "lucide-react";
|
|
5
|
+
import { loadState, saveState } from "./explorer-storage.js";
|
|
5
6
|
import { Input } from "../../ui/input.js";
|
|
6
7
|
import { Badge } from "../../ui/badge.js";
|
|
7
8
|
import { Button } from "../../ui/button.js";
|
|
@@ -43,10 +44,20 @@ function ApiEndpointsSidebar({
|
|
|
43
44
|
onToggleCollapse
|
|
44
45
|
}) {
|
|
45
46
|
const [searchQuery, setSearchQuery] = useState("");
|
|
46
|
-
const [expandedCategories, setExpandedCategories] = useState(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
const [expandedCategories, setExpandedCategories] = useState(() => {
|
|
48
|
+
const saved = loadState("sidebar:expandedCategories");
|
|
49
|
+
return new Set(saved ?? ["core", "entity", "theme", "plugin"]);
|
|
50
|
+
});
|
|
51
|
+
const [expandedPrefixes, setExpandedPrefixes] = useState(() => {
|
|
52
|
+
const saved = loadState("sidebar:expandedPrefixes");
|
|
53
|
+
return new Set(saved ?? []);
|
|
54
|
+
});
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
saveState("sidebar:expandedCategories", Array.from(expandedCategories));
|
|
57
|
+
}, [expandedCategories]);
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
saveState("sidebar:expandedPrefixes", Array.from(expandedPrefixes));
|
|
60
|
+
}, [expandedPrefixes]);
|
|
50
61
|
const filteredRoutes = useMemo(() => {
|
|
51
62
|
if (!searchQuery.trim()) return routes;
|
|
52
63
|
const query = searchQuery.toLowerCase();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiExplorer.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiExplorer.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;
|
|
1
|
+
{"version":3,"file":"ApiExplorer.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiExplorer.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAqB5F,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC,aAAa,EAAE,aAAa,EAAE,CAAC,CAAA;IAC9C,eAAe,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC1D;AAwBD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,gBAAgB,2CAibxE"}
|
|
@@ -13,6 +13,7 @@ import { useApiExplorerNavigation } from "../../../hooks/useApiExplorerNavigatio
|
|
|
13
13
|
import { extractPathParams, buildUrl } from "../api-tester/utils/url-builder.js";
|
|
14
14
|
import { ApiPresetsService } from "../../../lib/services/api-presets.service.js";
|
|
15
15
|
import { ApiDocsService } from "../../../lib/services/api-docs.service.js";
|
|
16
|
+
import { loadState, saveState, draftKey } from "./explorer-storage.js";
|
|
16
17
|
function findRouteByPath(routes, path) {
|
|
17
18
|
for (const category of Object.keys(routes)) {
|
|
18
19
|
const found = routes[category].find((r) => r.path === path);
|
|
@@ -29,23 +30,26 @@ function createEmptyRows(count = 2) {
|
|
|
29
30
|
}));
|
|
30
31
|
}
|
|
31
32
|
function ApiExplorer({ routes, initialEndpoint }) {
|
|
32
|
-
const [isCollapsed, setIsCollapsed] = useState(false);
|
|
33
|
+
const [isCollapsed, setIsCollapsed] = useState(() => loadState("sidebarCollapsed") ?? false);
|
|
33
34
|
const [mobileOpen, setMobileOpen] = useState(false);
|
|
34
35
|
const [selectedEndpoint, setSelectedEndpoint] = useState(null);
|
|
35
36
|
const [method, setMethod] = useState("GET");
|
|
36
37
|
const [pathParams, setPathParams] = useState([]);
|
|
37
38
|
const [queryParams, setQueryParams] = useState(() => createEmptyRows());
|
|
38
39
|
const [headers, setHeaders] = useState(() => createEmptyRows());
|
|
39
|
-
const [authType, setAuthType] = useState("session");
|
|
40
|
-
const [apiKey, setApiKey] = useState("");
|
|
41
|
-
const [bypassMode, setBypassMode] = useState(false);
|
|
40
|
+
const [authType, setAuthType] = useState(() => loadState("authType") ?? "session");
|
|
41
|
+
const [apiKey, setApiKey] = useState(() => loadState("apiKey") ?? "");
|
|
42
|
+
const [bypassMode, setBypassMode] = useState(() => loadState("bypassMode") ?? false);
|
|
42
43
|
const [selectedTeamId, setSelectedTeamId] = useState(() => {
|
|
44
|
+
const saved = loadState("selectedTeamId");
|
|
45
|
+
if (saved) return saved;
|
|
43
46
|
if (typeof window !== "undefined") {
|
|
44
47
|
return localStorage.getItem("activeTeamId");
|
|
45
48
|
}
|
|
46
49
|
return null;
|
|
47
50
|
});
|
|
48
51
|
const [body, setBody] = useState("");
|
|
52
|
+
const [actAsUserId, setActAsUserId] = useState(() => loadState("actAsUserId") ?? "");
|
|
49
53
|
const [tabsModifiedByPreset, setTabsModifiedByPreset] = useState(
|
|
50
54
|
() => /* @__PURE__ */ new Set()
|
|
51
55
|
);
|
|
@@ -60,20 +64,35 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
60
64
|
const hasInitialized = useRef(false);
|
|
61
65
|
const { status, response, error, execute, cancel, reset } = useApiRequest();
|
|
62
66
|
const { selectedMethod: urlMethod, selectedPath: urlPath, navigateToEndpoint } = useApiExplorerNavigation({ basePath: "/devtools/api" });
|
|
67
|
+
const applyEndpointState = useCallback((endpointMethod, path) => {
|
|
68
|
+
const draft = loadState(draftKey(endpointMethod, path));
|
|
69
|
+
setPathParams((draft == null ? void 0 : draft.pathParams) ?? extractPathParams(path));
|
|
70
|
+
setQueryParams((draft == null ? void 0 : draft.queryParams) ?? createEmptyRows());
|
|
71
|
+
setHeaders((draft == null ? void 0 : draft.headers) ?? createEmptyRows());
|
|
72
|
+
setBody((draft == null ? void 0 : draft.body) ?? "");
|
|
73
|
+
setTabsModifiedByPreset(/* @__PURE__ */ new Set());
|
|
74
|
+
}, []);
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
saveState("authType", authType);
|
|
77
|
+
saveState("apiKey", apiKey);
|
|
78
|
+
saveState("bypassMode", bypassMode);
|
|
79
|
+
saveState("selectedTeamId", selectedTeamId);
|
|
80
|
+
saveState("actAsUserId", actAsUserId);
|
|
81
|
+
}, [authType, apiKey, bypassMode, selectedTeamId, actAsUserId]);
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
saveState("sidebarCollapsed", isCollapsed);
|
|
84
|
+
}, [isCollapsed]);
|
|
63
85
|
const handleSelectEndpoint = useCallback(
|
|
64
86
|
(path, selectedMethod, route) => {
|
|
65
87
|
reset();
|
|
66
88
|
navigateToEndpoint(selectedMethod, path);
|
|
67
89
|
setSelectedEndpoint({ path, method: selectedMethod, route });
|
|
68
90
|
setMethod(selectedMethod);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
setHeaders(createEmptyRows());
|
|
72
|
-
setBody("");
|
|
73
|
-
setTabsModifiedByPreset(/* @__PURE__ */ new Set());
|
|
91
|
+
applyEndpointState(selectedMethod, path);
|
|
92
|
+
saveState("lastEndpoint", { method: selectedMethod, path });
|
|
74
93
|
setMobileOpen(false);
|
|
75
94
|
},
|
|
76
|
-
[reset, navigateToEndpoint]
|
|
95
|
+
[reset, navigateToEndpoint, applyEndpointState]
|
|
77
96
|
);
|
|
78
97
|
const handleSend = useCallback(async () => {
|
|
79
98
|
if (!selectedEndpoint) return;
|
|
@@ -90,6 +109,9 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
90
109
|
customHeaders["x-team-id"] = selectedTeamId;
|
|
91
110
|
}
|
|
92
111
|
}
|
|
112
|
+
if (actAsUserId.trim()) {
|
|
113
|
+
customHeaders["x-act-as-user"] = actAsUserId.trim();
|
|
114
|
+
}
|
|
93
115
|
for (const h of headers) {
|
|
94
116
|
if (h.enabled && h.key) {
|
|
95
117
|
customHeaders[h.key] = h.value;
|
|
@@ -103,7 +125,7 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
103
125
|
authType,
|
|
104
126
|
apiKey: authType === "apiKey" ? apiKey : void 0
|
|
105
127
|
});
|
|
106
|
-
}, [selectedEndpoint, pathParams, queryParams, headers, method, body, authType, apiKey, bypassMode, selectedTeamId, execute]);
|
|
128
|
+
}, [selectedEndpoint, pathParams, queryParams, headers, method, body, authType, apiKey, bypassMode, selectedTeamId, actAsUserId, execute]);
|
|
107
129
|
const handleApplyPreset = useCallback((preset) => {
|
|
108
130
|
const modifiedTabs = /* @__PURE__ */ new Set();
|
|
109
131
|
if (preset.method) {
|
|
@@ -168,14 +190,19 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
168
190
|
if (route && ((selectedEndpoint == null ? void 0 : selectedEndpoint.path) !== urlPath || (selectedEndpoint == null ? void 0 : selectedEndpoint.method) !== urlMethod)) {
|
|
169
191
|
setSelectedEndpoint({ path: urlPath, method: urlMethod, route });
|
|
170
192
|
setMethod(urlMethod);
|
|
171
|
-
|
|
172
|
-
setQueryParams(createEmptyRows());
|
|
173
|
-
setHeaders(createEmptyRows());
|
|
174
|
-
setBody("");
|
|
175
|
-
setTabsModifiedByPreset(/* @__PURE__ */ new Set());
|
|
193
|
+
applyEndpointState(urlMethod, urlPath);
|
|
176
194
|
}
|
|
177
195
|
}
|
|
178
|
-
}, [urlPath, urlMethod, routes, selectedEndpoint]);
|
|
196
|
+
}, [urlPath, urlMethod, routes, selectedEndpoint, applyEndpointState]);
|
|
197
|
+
useEffect(() => {
|
|
198
|
+
if (!selectedEndpoint) return;
|
|
199
|
+
saveState(draftKey(selectedEndpoint.method, selectedEndpoint.path), {
|
|
200
|
+
pathParams,
|
|
201
|
+
queryParams,
|
|
202
|
+
headers,
|
|
203
|
+
body
|
|
204
|
+
});
|
|
205
|
+
}, [selectedEndpoint, pathParams, queryParams, headers, body]);
|
|
179
206
|
useEffect(() => {
|
|
180
207
|
if (hasInitialized.current) return;
|
|
181
208
|
if (urlPath && urlMethod) {
|
|
@@ -184,7 +211,7 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
184
211
|
hasInitialized.current = true;
|
|
185
212
|
setSelectedEndpoint({ path: urlPath, method: urlMethod, route });
|
|
186
213
|
setMethod(urlMethod);
|
|
187
|
-
|
|
214
|
+
applyEndpointState(urlMethod, urlPath);
|
|
188
215
|
return;
|
|
189
216
|
}
|
|
190
217
|
}
|
|
@@ -195,13 +222,26 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
195
222
|
const endpointMethod = initialEndpoint.method;
|
|
196
223
|
setSelectedEndpoint({ path: initialEndpoint.path, method: endpointMethod, route });
|
|
197
224
|
setMethod(endpointMethod);
|
|
198
|
-
|
|
225
|
+
applyEndpointState(endpointMethod, initialEndpoint.path);
|
|
199
226
|
if (!urlPath) {
|
|
200
227
|
navigateToEndpoint(endpointMethod, initialEndpoint.path);
|
|
201
228
|
}
|
|
202
229
|
return;
|
|
203
230
|
}
|
|
204
231
|
}
|
|
232
|
+
const last = loadState("lastEndpoint");
|
|
233
|
+
if (last) {
|
|
234
|
+
const route = findRouteByPath(routes, last.path);
|
|
235
|
+
if (route) {
|
|
236
|
+
hasInitialized.current = true;
|
|
237
|
+
const lastMethod = last.method;
|
|
238
|
+
setSelectedEndpoint({ path: last.path, method: lastMethod, route });
|
|
239
|
+
setMethod(lastMethod);
|
|
240
|
+
applyEndpointState(lastMethod, last.path);
|
|
241
|
+
navigateToEndpoint(lastMethod, last.path);
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
205
245
|
const categories = ["core", "entity", "theme", "plugin"];
|
|
206
246
|
for (const category of categories) {
|
|
207
247
|
const categoryRoutes = routes[category];
|
|
@@ -211,12 +251,12 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
211
251
|
const firstMethod = firstRoute.methods[0] || "GET";
|
|
212
252
|
setSelectedEndpoint({ path: firstRoute.path, method: firstMethod, route: firstRoute });
|
|
213
253
|
setMethod(firstMethod);
|
|
214
|
-
|
|
254
|
+
applyEndpointState(firstMethod, firstRoute.path);
|
|
215
255
|
navigateToEndpoint(firstMethod, firstRoute.path);
|
|
216
256
|
break;
|
|
217
257
|
}
|
|
218
258
|
}
|
|
219
|
-
}, [routes, initialEndpoint, urlPath, urlMethod, navigateToEndpoint]);
|
|
259
|
+
}, [routes, initialEndpoint, urlPath, urlMethod, navigateToEndpoint, applyEndpointState]);
|
|
220
260
|
const totalRoutes = Object.values(routes).reduce((sum, arr) => sum + arr.length, 0);
|
|
221
261
|
return /* @__PURE__ */ jsxs("div", { className: "flex h-[calc(100vh-4rem)] w-full overflow-hidden", "data-cy": sel("devtools.apiExplorer.container"), children: [
|
|
222
262
|
/* @__PURE__ */ jsxs("div", { className: "lg:hidden fixed top-0 left-0 right-0 z-50 h-14 bg-background border-b flex items-center px-4 gap-3", children: [
|
|
@@ -281,6 +321,7 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
281
321
|
apiKey,
|
|
282
322
|
bypassMode,
|
|
283
323
|
selectedTeamId,
|
|
324
|
+
actAsUserId,
|
|
284
325
|
body,
|
|
285
326
|
status,
|
|
286
327
|
onMethodChange: setMethod,
|
|
@@ -291,6 +332,7 @@ function ApiExplorer({ routes, initialEndpoint }) {
|
|
|
291
332
|
onApiKeyChange: setApiKey,
|
|
292
333
|
onBypassModeChange: setBypassMode,
|
|
293
334
|
onTeamChange: setSelectedTeamId,
|
|
335
|
+
onActAsUserChange: setActAsUserId,
|
|
294
336
|
onBodyChange: setBody,
|
|
295
337
|
onSend: handleSend,
|
|
296
338
|
onCancel: cancel,
|
|
@@ -12,6 +12,7 @@ interface ApiRequestPanelProps {
|
|
|
12
12
|
apiKey: string;
|
|
13
13
|
bypassMode: boolean;
|
|
14
14
|
selectedTeamId: string | null;
|
|
15
|
+
actAsUserId: string;
|
|
15
16
|
body: string;
|
|
16
17
|
status: RequestStatus;
|
|
17
18
|
onMethodChange: (method: HttpMethod) => void;
|
|
@@ -22,6 +23,7 @@ interface ApiRequestPanelProps {
|
|
|
22
23
|
onApiKeyChange: (key: string) => void;
|
|
23
24
|
onBypassModeChange: (enabled: boolean) => void;
|
|
24
25
|
onTeamChange: (teamId: string | null) => void;
|
|
26
|
+
onActAsUserChange: (userId: string) => void;
|
|
25
27
|
onBodyChange: (body: string) => void;
|
|
26
28
|
onSend: () => void;
|
|
27
29
|
onCancel: () => void;
|
|
@@ -34,6 +36,6 @@ interface ApiRequestPanelProps {
|
|
|
34
36
|
tabsModifiedByPreset?: Set<'params' | 'headers' | 'body'>;
|
|
35
37
|
onClearPresetIndicator?: (tab: 'params' | 'headers' | 'body') => void;
|
|
36
38
|
}
|
|
37
|
-
export declare function ApiRequestPanel({ route, basePath, method, pathParams, queryParams, headers, authType, apiKey, bypassMode, selectedTeamId, body, status, onMethodChange, onPathParamsChange, onQueryParamsChange, onHeadersChange, onAuthTypeChange, onApiKeyChange, onBypassModeChange, onTeamChange, onBodyChange, onSend, onCancel, endpointPresets, endpointDoc, onApplyPreset, tabsModifiedByPreset, onClearPresetIndicator, }: ApiRequestPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
export declare function ApiRequestPanel({ route, basePath, method, pathParams, queryParams, headers, authType, apiKey, bypassMode, selectedTeamId, actAsUserId, body, status, onMethodChange, onPathParamsChange, onQueryParamsChange, onHeadersChange, onAuthTypeChange, onApiKeyChange, onBypassModeChange, onTeamChange, onActAsUserChange, onBodyChange, onSend, onCancel, endpointPresets, endpointDoc, onApplyPreset, tabsModifiedByPreset, onClearPresetIndicator, }: ApiRequestPanelProps): import("react/jsx-runtime").JSX.Element;
|
|
38
40
|
export {};
|
|
39
41
|
//# sourceMappingURL=ApiRequestPanel.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiRequestPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiRequestPanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAa/E,UAAU,oBAAoB;IAC5B,KAAK,EAAE,aAAa,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,UAAU,CAAA;IAClB,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,WAAW,EAAE,YAAY,EAAE,CAAA;IAC3B,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC5C,kBAAkB,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IACjD,mBAAmB,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IACrD,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IAClD,gBAAgB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC1C,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAA;IAEpB,eAAe,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC3C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpD,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;IAE3C,oBAAoB,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,CAAA;IACzD,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,KAAK,IAAI,CAAA;CACtE;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,WAAW,EACX,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,cAAc,EACd,IAAI,EACJ,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,sBAAsB,GACvB,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"ApiRequestPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiRequestPanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACvG,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0CAA0C,CAAA;AAC7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AAa/E,UAAU,oBAAoB;IAC5B,KAAK,EAAE,aAAa,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,UAAU,CAAA;IAClB,UAAU,EAAE,SAAS,EAAE,CAAA;IACvB,WAAW,EAAE,YAAY,EAAE,CAAA;IAC3B,OAAO,EAAE,YAAY,EAAE,CAAA;IACvB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,aAAa,CAAA;IACrB,cAAc,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,CAAA;IAC5C,kBAAkB,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAA;IACjD,mBAAmB,EAAE,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IACrD,eAAe,EAAE,CAAC,OAAO,EAAE,YAAY,EAAE,KAAK,IAAI,CAAA;IAClD,gBAAgB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC1C,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IAC3C,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACpC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,QAAQ,EAAE,MAAM,IAAI,CAAA;IAEpB,eAAe,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAC3C,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpD,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,KAAK,IAAI,CAAA;IAE3C,oBAAoB,CAAC,EAAE,GAAG,CAAC,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,CAAA;IACzD,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,KAAK,IAAI,CAAA;CACtE;AAED,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,WAAW,EACX,OAAO,EACP,QAAQ,EACR,MAAM,EACN,UAAU,EACV,cAAc,EACd,WAAW,EACX,IAAI,EACJ,MAAM,EACN,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,MAAM,EACN,QAAQ,EACR,eAAe,EACf,WAAW,EACX,aAAa,EACb,oBAAoB,EACpB,sBAAsB,GACvB,EAAE,oBAAoB,2CAgOtB"}
|
|
@@ -36,6 +36,7 @@ function ApiRequestPanel({
|
|
|
36
36
|
apiKey,
|
|
37
37
|
bypassMode,
|
|
38
38
|
selectedTeamId,
|
|
39
|
+
actAsUserId,
|
|
39
40
|
body,
|
|
40
41
|
status,
|
|
41
42
|
onMethodChange,
|
|
@@ -46,6 +47,7 @@ function ApiRequestPanel({
|
|
|
46
47
|
onApiKeyChange,
|
|
47
48
|
onBypassModeChange,
|
|
48
49
|
onTeamChange,
|
|
50
|
+
onActAsUserChange,
|
|
49
51
|
onBodyChange,
|
|
50
52
|
onSend,
|
|
51
53
|
onCancel,
|
|
@@ -132,10 +134,12 @@ function ApiRequestPanel({
|
|
|
132
134
|
apiKey,
|
|
133
135
|
bypassMode,
|
|
134
136
|
selectedTeamId,
|
|
137
|
+
actAsUserId,
|
|
135
138
|
onAuthTypeChange,
|
|
136
139
|
onApiKeyChange,
|
|
137
140
|
onBypassModeChange,
|
|
138
|
-
onTeamChange
|
|
141
|
+
onTeamChange,
|
|
142
|
+
onActAsUserChange
|
|
139
143
|
}
|
|
140
144
|
)
|
|
141
145
|
] }),
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Small localStorage helper for the API Explorer.
|
|
3
|
+
*
|
|
4
|
+
* Persists the Explorer's working state — session/auth config and per-endpoint
|
|
5
|
+
* request drafts — across reloads and endpoint switches. All access is SSR-safe
|
|
6
|
+
* and swallows quota/parse errors.
|
|
7
|
+
*/
|
|
8
|
+
export declare function loadState<T>(key: string): T | null;
|
|
9
|
+
export declare function saveState(key: string, value: unknown): void;
|
|
10
|
+
/** localStorage key for a specific endpoint's saved request draft. */
|
|
11
|
+
export declare function draftKey(method: string, path: string): string;
|
|
12
|
+
//# sourceMappingURL=explorer-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"explorer-storage.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/explorer-storage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAQlD;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAO3D;AAED,sEAAsE;AACtE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAE7D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const PREFIX = "nextspark:apiExplorer:";
|
|
2
|
+
function loadState(key) {
|
|
3
|
+
if (typeof window === "undefined") return null;
|
|
4
|
+
try {
|
|
5
|
+
const raw = window.localStorage.getItem(PREFIX + key);
|
|
6
|
+
return raw ? JSON.parse(raw) : null;
|
|
7
|
+
} catch {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function saveState(key, value) {
|
|
12
|
+
if (typeof window === "undefined") return;
|
|
13
|
+
try {
|
|
14
|
+
window.localStorage.setItem(PREFIX + key, JSON.stringify(value));
|
|
15
|
+
} catch {
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function draftKey(method, path) {
|
|
19
|
+
return `draft:${method}:${path}`;
|
|
20
|
+
}
|
|
21
|
+
export {
|
|
22
|
+
draftKey,
|
|
23
|
+
loadState,
|
|
24
|
+
saveState
|
|
25
|
+
};
|
|
@@ -4,11 +4,14 @@ interface AuthSelectorProps {
|
|
|
4
4
|
apiKey: string;
|
|
5
5
|
bypassMode: boolean;
|
|
6
6
|
selectedTeamId: string | null;
|
|
7
|
+
/** Optional. The act-as input renders only when onActAsUserChange is provided. */
|
|
8
|
+
actAsUserId?: string;
|
|
7
9
|
onAuthTypeChange: (type: AuthType) => void;
|
|
8
10
|
onApiKeyChange: (key: string) => void;
|
|
9
11
|
onBypassModeChange: (enabled: boolean) => void;
|
|
10
12
|
onTeamChange: (teamId: string | null) => void;
|
|
13
|
+
onActAsUserChange?: (userId: string) => void;
|
|
11
14
|
}
|
|
12
|
-
export declare function AuthSelector({ authType, apiKey, bypassMode, selectedTeamId, onAuthTypeChange, onApiKeyChange, onBypassModeChange, onTeamChange, }: AuthSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function AuthSelector({ authType, apiKey, bypassMode, selectedTeamId, actAsUserId, onAuthTypeChange, onApiKeyChange, onBypassModeChange, onTeamChange, onActAsUserChange, }: AuthSelectorProps): import("react/jsx-runtime").JSX.Element;
|
|
13
16
|
export {};
|
|
14
17
|
//# sourceMappingURL=AuthSelector.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthSelector.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-tester/AuthSelector.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC,UAAU,iBAAiB;IACzB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,gBAAgB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC1C,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"AuthSelector.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-tester/AuthSelector.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGvC,UAAU,iBAAiB;IACzB,QAAQ,EAAE,QAAQ,CAAA;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gBAAgB,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAA;IAC1C,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,kBAAkB,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAA;IAC9C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC7C,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;CAC7C;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,MAAM,EACN,UAAU,EACV,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,EACZ,iBAAiB,GAClB,EAAE,iBAAiB,2CAoGnB"}
|
|
@@ -19,10 +19,12 @@ function AuthSelector({
|
|
|
19
19
|
apiKey,
|
|
20
20
|
bypassMode,
|
|
21
21
|
selectedTeamId,
|
|
22
|
+
actAsUserId,
|
|
22
23
|
onAuthTypeChange,
|
|
23
24
|
onApiKeyChange,
|
|
24
25
|
onBypassModeChange,
|
|
25
|
-
onTeamChange
|
|
26
|
+
onTeamChange,
|
|
27
|
+
onActAsUserChange
|
|
26
28
|
}) {
|
|
27
29
|
const t = useTranslations("devtools.apiTester.auth");
|
|
28
30
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-3", "data-cy": sel("devtools.apiExplorer.auth.container"), children: [
|
|
@@ -79,7 +81,27 @@ function AuthSelector({
|
|
|
79
81
|
onTeamChange,
|
|
80
82
|
bypassMode
|
|
81
83
|
}
|
|
82
|
-
)
|
|
84
|
+
),
|
|
85
|
+
onActAsUserChange && /* @__PURE__ */ jsxs("div", { className: "space-y-1.5 pt-2 border-t", children: [
|
|
86
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
87
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "act-as-user", className: "text-sm cursor-pointer", children: "Act as user" }),
|
|
88
|
+
/* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
89
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Info, { className: "h-4 w-4 text-muted-foreground cursor-help" }) }),
|
|
90
|
+
/* @__PURE__ */ jsx(TooltipContent, { className: "max-w-xs", children: "Developer-only. Resolves session-scoped endpoints (e.g. /me/*) as the given user without switching your session. Requires the developer role, a non-production environment, and ALLOW_ACT_AS_USER=true. Leave empty to use your own session." })
|
|
91
|
+
] }) })
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
Input,
|
|
95
|
+
{
|
|
96
|
+
id: "act-as-user",
|
|
97
|
+
placeholder: "user id (empty = your own session)",
|
|
98
|
+
value: actAsUserId ?? "",
|
|
99
|
+
onChange: (e) => onActAsUserChange(e.target.value),
|
|
100
|
+
className: "font-mono text-sm",
|
|
101
|
+
"data-cy": "devtools-api-auth-act-as-user"
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
] })
|
|
83
105
|
] });
|
|
84
106
|
}
|
|
85
107
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"docs-content.d.ts","sourceRoot":"","sources":["../../../src/components/docs/docs-content.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"docs-content.d.ts","sourceRoot":"","sources":["../../../src/components/docs/docs-content.tsx"],"names":[],"mappings":"AAKA,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CA4BrD"}
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { renderMermaid } from "../../lib/docs/mermaid.js";
|
|
3
5
|
function DocsContent({ html }) {
|
|
6
|
+
const containerRef = useRef(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const container = containerRef.current;
|
|
9
|
+
if (!container) return;
|
|
10
|
+
const nodes = Array.from(
|
|
11
|
+
container.querySelectorAll(
|
|
12
|
+
'pre.mermaid:not([data-processed="true"])'
|
|
13
|
+
)
|
|
14
|
+
);
|
|
15
|
+
if (nodes.length === 0) return;
|
|
16
|
+
renderMermaid(nodes).catch((error) => {
|
|
17
|
+
console.error("[DocsContent] Failed to render mermaid diagrams:", error);
|
|
18
|
+
});
|
|
19
|
+
}, [html]);
|
|
4
20
|
return /* @__PURE__ */ jsx(
|
|
5
21
|
"div",
|
|
6
22
|
{
|
|
23
|
+
ref: containerRef,
|
|
7
24
|
className: "docs-content",
|
|
8
25
|
dangerouslySetInnerHTML: { __html: html },
|
|
9
26
|
"data-cy": "docs-content"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
interface MermaidDiagramProps {
|
|
2
|
+
chart: string;
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Renders a single mermaid diagram from its source. Used by React-based
|
|
6
|
+
* markdown renderers (e.g. MarkdownViewer) where a ```mermaid fence maps to a
|
|
7
|
+
* React element rather than to an HTML marker string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function MermaidDiagram({ chart }: MermaidDiagramProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=mermaid-diagram.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mermaid-diagram.d.ts","sourceRoot":"","sources":["../../../src/components/docs/mermaid-diagram.tsx"],"names":[],"mappings":"AAKA,UAAU,mBAAmB;IAC3B,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,KAAK,EAAE,EAAE,mBAAmB,2CAgB5D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef } from "react";
|
|
4
|
+
import { renderMermaid } from "../../lib/docs/mermaid.js";
|
|
5
|
+
function MermaidDiagram({ chart }) {
|
|
6
|
+
const ref = useRef(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const node = ref.current;
|
|
9
|
+
if (!node) return;
|
|
10
|
+
node.removeAttribute("data-processed");
|
|
11
|
+
node.textContent = chart;
|
|
12
|
+
renderMermaid([node]).catch((error) => {
|
|
13
|
+
console.error("[MermaidDiagram] Failed to render diagram:", error);
|
|
14
|
+
});
|
|
15
|
+
}, [chart]);
|
|
16
|
+
return /* @__PURE__ */ jsx("pre", { ref, className: "mermaid" });
|
|
17
|
+
}
|
|
18
|
+
export {
|
|
19
|
+
MermaidDiagram
|
|
20
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useApiExplorerNavigation.d.ts","sourceRoot":"","sources":["../../src/hooks/useApiExplorerNavigation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAA;AAEzE,MAAM,WAAW,+BAA+B;IAC9C,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAA;IAChB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC5E;AAED,MAAM,WAAW,8BAA8B;IAC7C,qCAAqC;IACrC,cAAc,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC,8DAA8D;IAC9D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,8BAA8B;IAC9B,kBAAkB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D,yCAAyC;IACzC,cAAc,EAAE,MAAM,IAAI,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,8BAA8B,
|
|
1
|
+
{"version":3,"file":"useApiExplorerNavigation.d.ts","sourceRoot":"","sources":["../../src/hooks/useApiExplorerNavigation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yCAAyC,CAAA;AAEzE,MAAM,WAAW,+BAA+B;IAC9C,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,CAAA;IAChB,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC5E;AAED,MAAM,WAAW,8BAA8B;IAC7C,qCAAqC;IACrC,cAAc,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC,8DAA8D;IAC9D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,8BAA8B;IAC9B,kBAAkB,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9D,yCAAyC;IACzC,cAAc,EAAE,MAAM,IAAI,CAAA;CAC3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,8BAA8B,CAiEhC"}
|
|
@@ -29,7 +29,11 @@ function useApiExplorerNavigation(options) {
|
|
|
29
29
|
(method, path) => {
|
|
30
30
|
const pathSegments = path.replace(/^\//, "");
|
|
31
31
|
const newUrl = `${basePath}/${method}/${pathSegments}`;
|
|
32
|
-
|
|
32
|
+
if (typeof window !== "undefined") {
|
|
33
|
+
window.history.pushState(null, "", newUrl);
|
|
34
|
+
} else {
|
|
35
|
+
router.push(newUrl);
|
|
36
|
+
}
|
|
33
37
|
},
|
|
34
38
|
[basePath, router]
|
|
35
39
|
);
|
|
@@ -15,6 +15,15 @@ export declare const SYSTEM_ADMIN_TEAM_ID = "team-nextspark-001";
|
|
|
15
15
|
*/
|
|
16
16
|
export declare const ADMIN_BYPASS_HEADER = "x-admin-bypass";
|
|
17
17
|
export declare const ADMIN_BYPASS_VALUE = "confirm-cross-team-access";
|
|
18
|
+
/**
|
|
19
|
+
* Development-only override header: a developer caller can resolve the request
|
|
20
|
+
* AS a different user by sending `x-act-as-user: <userId>`. Useful for
|
|
21
|
+
* exercising session-scoped ("me"/personal) endpoints as any user without
|
|
22
|
+
* switching sessions. Strictly non-production and developer-role gated (see
|
|
23
|
+
* applyActAsOverride). Core-level and theme-agnostic — every endpoint that
|
|
24
|
+
* authenticates via authenticateRequest honors it.
|
|
25
|
+
*/
|
|
26
|
+
export declare const ACT_AS_USER_HEADER = "x-act-as-user";
|
|
18
27
|
export interface DualAuthUser {
|
|
19
28
|
id: string;
|
|
20
29
|
email: string;
|
|
@@ -28,6 +37,11 @@ export interface DualAuthResult {
|
|
|
28
37
|
user: DualAuthUser | null;
|
|
29
38
|
scopes?: string[];
|
|
30
39
|
rateLimitResponse?: Response;
|
|
40
|
+
/** Set when a dev-only x-act-as-user override replaced the real caller. */
|
|
41
|
+
actingAs?: {
|
|
42
|
+
originalUserId: string;
|
|
43
|
+
originalRole: string;
|
|
44
|
+
};
|
|
31
45
|
}
|
|
32
46
|
/**
|
|
33
47
|
* Try to authenticate request using either API Key or Session
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dual-auth.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/dual-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AASvD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,uBAAuB,CAAA;AAExD;;GAEG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AACnD,eAAO,MAAM,kBAAkB,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"dual-auth.d.ts","sourceRoot":"","sources":["../../../../src/lib/api/auth/dual-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AASvD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,uBAAuB,CAAA;AAExD;;GAEG;AACH,eAAO,MAAM,mBAAmB,mBAAmB,CAAA;AACnD,eAAO,MAAM,kBAAkB,8BAA8B,CAAA;AAE7D;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,kBAAkB,CAAA;AAOjD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;IACpC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAA;IACjB,iBAAiB,CAAC,EAAE,QAAQ,CAAA;IAC5B,2EAA2E;IAC3E,QAAQ,CAAC,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAA;CAC5D;AAqBD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBvF;AAqKD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAY3F;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,WAAW,EACpB,UAAU,EAAE,cAAc,GACzB,OAAO,CAAC,MAAM,GAAG,YAAY,CAAC,CAqBhC;AAMD;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,cAAc,EAC1B,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAkBD;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,MAAkC,EAAE,MAAM,GAAE,MAAY;;;;GAShG"}
|
|
@@ -6,6 +6,7 @@ import { TeamMemberService } from "../../services/team-member.service.js";
|
|
|
6
6
|
const SYSTEM_ADMIN_TEAM_ID = "team-nextspark-001";
|
|
7
7
|
const ADMIN_BYPASS_HEADER = "x-admin-bypass";
|
|
8
8
|
const ADMIN_BYPASS_VALUE = "confirm-cross-team-access";
|
|
9
|
+
const ACT_AS_USER_HEADER = "x-act-as-user";
|
|
9
10
|
const ELEVATED_ROLES = ["superadmin", "developer"];
|
|
10
11
|
async function getUserDefaultTeamId(userId) {
|
|
11
12
|
try {
|
|
@@ -25,11 +26,11 @@ async function getUserDefaultTeamId(userId) {
|
|
|
25
26
|
async function authenticateRequest(request) {
|
|
26
27
|
const apiKeyResult = await tryApiKeyAuth(request);
|
|
27
28
|
if (apiKeyResult.success) {
|
|
28
|
-
return apiKeyResult;
|
|
29
|
+
return applyActAsOverride(request, apiKeyResult);
|
|
29
30
|
}
|
|
30
31
|
const sessionResult = await trySessionAuth(request);
|
|
31
32
|
if (sessionResult.success) {
|
|
32
|
-
return sessionResult;
|
|
33
|
+
return applyActAsOverride(request, sessionResult);
|
|
33
34
|
}
|
|
34
35
|
return {
|
|
35
36
|
success: false,
|
|
@@ -37,6 +38,45 @@ async function authenticateRequest(request) {
|
|
|
37
38
|
user: null
|
|
38
39
|
};
|
|
39
40
|
}
|
|
41
|
+
function isProductionRuntime() {
|
|
42
|
+
if (process.env.VERCEL_ENV) return process.env.VERCEL_ENV === "production";
|
|
43
|
+
return process.env.NODE_ENV === "production";
|
|
44
|
+
}
|
|
45
|
+
async function applyActAsOverride(request, authResult) {
|
|
46
|
+
if (isProductionRuntime()) return authResult;
|
|
47
|
+
if (process.env.ALLOW_ACT_AS_USER !== "true") return authResult;
|
|
48
|
+
const targetUserId = request.headers.get(ACT_AS_USER_HEADER);
|
|
49
|
+
if (!targetUserId) return authResult;
|
|
50
|
+
if (!authResult.user || authResult.user.role !== "developer") return authResult;
|
|
51
|
+
try {
|
|
52
|
+
const target = await queryOne(
|
|
53
|
+
'SELECT email, role, name FROM "users" WHERE id = $1',
|
|
54
|
+
[targetUserId]
|
|
55
|
+
);
|
|
56
|
+
if (!target) return authResult;
|
|
57
|
+
const defaultTeamId = await getUserDefaultTeamId(targetUserId);
|
|
58
|
+
console.warn(
|
|
59
|
+
`[dual-auth] ACT-AS (dev only): ${authResult.user.email} (${authResult.user.role}) -> acting as user ${targetUserId}`
|
|
60
|
+
);
|
|
61
|
+
return {
|
|
62
|
+
...authResult,
|
|
63
|
+
user: {
|
|
64
|
+
id: targetUserId,
|
|
65
|
+
email: target.email || "",
|
|
66
|
+
role: target.role || "user",
|
|
67
|
+
name: target.name,
|
|
68
|
+
defaultTeamId
|
|
69
|
+
},
|
|
70
|
+
actingAs: {
|
|
71
|
+
originalUserId: authResult.user.id,
|
|
72
|
+
originalRole: authResult.user.role
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
} catch (error) {
|
|
76
|
+
console.error("[dual-auth] act-as override failed; ignoring:", error);
|
|
77
|
+
return authResult;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
40
80
|
async function tryApiKeyAuth(request) {
|
|
41
81
|
try {
|
|
42
82
|
const authHeader = request.headers.get("authorization");
|
|
@@ -163,6 +203,7 @@ function createAuthError(message = "Authentication required", status = 401) {
|
|
|
163
203
|
);
|
|
164
204
|
}
|
|
165
205
|
export {
|
|
206
|
+
ACT_AS_USER_HEADER,
|
|
166
207
|
ADMIN_BYPASS_HEADER,
|
|
167
208
|
ADMIN_BYPASS_VALUE,
|
|
168
209
|
SYSTEM_ADMIN_TEAM_ID,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared client-side Mermaid rendering.
|
|
3
|
+
*
|
|
4
|
+
* Turns <pre class="mermaid"> markers (which carry the raw diagram source as
|
|
5
|
+
* their text content) into inline SVG, and enforces responsive sizing so
|
|
6
|
+
* diagrams never overflow. Used by every Core markdown surface — the docs HTML
|
|
7
|
+
* pipeline (DocsContent), the React-based MarkdownViewer, and the API docs
|
|
8
|
+
* modal — so they all render mermaid identically.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Render the given <pre class="mermaid"> nodes to SVG. Nodes mermaid has already
|
|
12
|
+
* processed are skipped, so this is safe to call repeatedly.
|
|
13
|
+
*/
|
|
14
|
+
export declare function renderMermaid(nodes: HTMLElement[]): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=mermaid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../../src/lib/docs/mermaid.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH;;;GAGG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqBvE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
let mermaidPromise = null;
|
|
2
|
+
function getMermaid() {
|
|
3
|
+
if (!mermaidPromise) {
|
|
4
|
+
mermaidPromise = import("mermaid").then((mod) => mod.default);
|
|
5
|
+
}
|
|
6
|
+
return mermaidPromise;
|
|
7
|
+
}
|
|
8
|
+
function resolveTheme() {
|
|
9
|
+
if (typeof document !== "undefined") {
|
|
10
|
+
const el = document.documentElement;
|
|
11
|
+
if (el.classList.contains("dark")) return "dark";
|
|
12
|
+
if (el.classList.contains("light")) return "default";
|
|
13
|
+
const attr = el.getAttribute("data-theme");
|
|
14
|
+
if (attr === "dark") return "dark";
|
|
15
|
+
if (attr === "light") return "default";
|
|
16
|
+
}
|
|
17
|
+
if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
|
|
18
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "default";
|
|
19
|
+
}
|
|
20
|
+
return "default";
|
|
21
|
+
}
|
|
22
|
+
async function renderMermaid(nodes) {
|
|
23
|
+
const pending = nodes.filter((node) => node.getAttribute("data-processed") !== "true");
|
|
24
|
+
if (pending.length === 0) return;
|
|
25
|
+
const mermaid = await getMermaid();
|
|
26
|
+
mermaid.initialize({
|
|
27
|
+
startOnLoad: false,
|
|
28
|
+
securityLevel: "strict",
|
|
29
|
+
theme: resolveTheme()
|
|
30
|
+
});
|
|
31
|
+
await mermaid.run({ nodes: pending, suppressErrors: true });
|
|
32
|
+
for (const node of pending) {
|
|
33
|
+
const svg = node.querySelector("svg");
|
|
34
|
+
if (svg) {
|
|
35
|
+
svg.style.maxWidth = "100%";
|
|
36
|
+
svg.style.height = "auto";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
renderMermaid
|
|
42
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/lib/docs/parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/lib/docs/parser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,EACL,wBAAwB,EACxB,aAAa,EACb,WAAW,EACZ,MAAM,SAAS,CAAA;AAEhB;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,WAAW,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,QAAQ,GAAG,YAAY,CAAA;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,OAAO,EAAE,CAAA;IAChB,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IACjE,QAAQ,EAAE,WAAW,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb,CAAC,CAiBD"}
|
package/dist/lib/docs/parser.js
CHANGED
|
@@ -5,6 +5,7 @@ import remarkGfm from "remark-gfm";
|
|
|
5
5
|
import remarkRehype from "remark-rehype";
|
|
6
6
|
import rehypeShiki from "@shikijs/rehype";
|
|
7
7
|
import rehypeStringify from "rehype-stringify";
|
|
8
|
+
import { remarkMermaid } from "./remark-mermaid.js";
|
|
8
9
|
import {
|
|
9
10
|
extractOrderFromFilename,
|
|
10
11
|
cleanFilename,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
async function parseMarkdownFile(filePath) {
|
|
14
15
|
const fileContent = fs.readFileSync(filePath, "utf-8");
|
|
15
16
|
const { data, content } = matter(fileContent);
|
|
16
|
-
const processedContent = await remark().use(remarkGfm).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeShiki, { theme: "github-dark" }).use(rehypeStringify, { allowDangerousHtml: true }).process(content);
|
|
17
|
+
const processedContent = await remark().use(remarkGfm).use(remarkMermaid).use(remarkRehype, { allowDangerousHtml: true }).use(rehypeShiki, { theme: "github-dark" }).use(rehypeStringify, { allowDangerousHtml: true }).process(content);
|
|
17
18
|
return {
|
|
18
19
|
metadata: data,
|
|
19
20
|
content,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* remark plugin: rewrite ```mermaid fenced code blocks into a neutral
|
|
3
|
+
* <pre class="mermaid"> marker that carries the raw diagram source, instead of
|
|
4
|
+
* letting the syntax highlighter render them as source code.
|
|
5
|
+
*
|
|
6
|
+
* The marker is platform-neutral: the diagram source is preserved verbatim so
|
|
7
|
+
* each surface can render it its own way. On web it is picked up and rendered
|
|
8
|
+
* to SVG client-side after mount (see DocsContent).
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Unified/remark plugin. Runs on the mdast tree before it is converted to
|
|
12
|
+
* rehype, so the mermaid fence never reaches the syntax highlighter.
|
|
13
|
+
*/
|
|
14
|
+
export declare function remarkMermaid(): (tree: unknown) => void;
|
|
15
|
+
//# sourceMappingURL=remark-mermaid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remark-mermaid.d.ts","sourceRoot":"","sources":["../../../src/lib/docs/remark-mermaid.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuCH;;;GAGG;AACH,wBAAgB,aAAa,KACnB,MAAM,OAAO,KAAG,IAAI,CAG7B"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function escapeHtml(source) {
|
|
2
|
+
return source.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
3
|
+
}
|
|
4
|
+
function transformMermaidFences(node) {
|
|
5
|
+
var _a;
|
|
6
|
+
if (!node.children) return;
|
|
7
|
+
for (let index = 0; index < node.children.length; index++) {
|
|
8
|
+
const child = node.children[index];
|
|
9
|
+
if (child.type === "code" && ((_a = child.lang) == null ? void 0 : _a.toLowerCase()) === "mermaid") {
|
|
10
|
+
node.children[index] = {
|
|
11
|
+
type: "html",
|
|
12
|
+
value: `<pre class="mermaid">${escapeHtml(child.value ?? "")}</pre>`
|
|
13
|
+
};
|
|
14
|
+
} else {
|
|
15
|
+
transformMermaidFences(child);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function remarkMermaid() {
|
|
20
|
+
return (tree) => {
|
|
21
|
+
transformMermaidFences(tree);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
remarkMermaid
|
|
26
|
+
};
|
package/dist/styles/classes.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
|
-
"generated": "2026-
|
|
3
|
-
"totalClasses":
|
|
2
|
+
"generated": "2026-07-01T20:08:31.259Z",
|
|
3
|
+
"totalClasses": 1082,
|
|
4
4
|
"classes": [
|
|
5
5
|
"!text-2xl",
|
|
6
6
|
"''",
|
|
@@ -644,6 +644,7 @@
|
|
|
644
644
|
"md:text-left",
|
|
645
645
|
"md:w-auto",
|
|
646
646
|
"medium",
|
|
647
|
+
"mermaid",
|
|
647
648
|
"min-h-0",
|
|
648
649
|
"min-h-10",
|
|
649
650
|
"min-h-[100px]",
|
|
@@ -2,6 +2,8 @@ import type { Metadata } from 'next'
|
|
|
2
2
|
import { LoginForm } from '@nextsparkjs/core/components/auth/forms/LoginForm'
|
|
3
3
|
import { getTemplateOrDefault, getMetadataOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
4
4
|
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
|
|
5
7
|
const defaultMetadata: Metadata = {
|
|
6
8
|
title: 'Sign In',
|
|
7
9
|
description: 'Sign in to your account to access the platform',
|
|
@@ -6,6 +6,10 @@ import { getTemplateOrDefault, getMetadataOrDefault } from '@nextsparkjs/core/li
|
|
|
6
6
|
import { AUTH_CONFIG } from '@nextsparkjs/core/lib/config'
|
|
7
7
|
import { TeamService } from '@nextsparkjs/core/lib/services'
|
|
8
8
|
|
|
9
|
+
// This page reads DB state (TeamService.hasGlobal) and redirect()s during render,
|
|
10
|
+
// so it must render per-request and never be statically prerendered at build time.
|
|
11
|
+
export const dynamic = 'force-dynamic'
|
|
12
|
+
|
|
9
13
|
const defaultMetadata: Metadata = {
|
|
10
14
|
title: 'Create Account',
|
|
11
15
|
description: 'Create your account to start using our platform',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextsparkjs/core",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.178",
|
|
4
4
|
"description": "NextSpark - The complete SaaS framework for Next.js",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "NextSpark <hello@nextspark.dev>",
|
|
@@ -449,6 +449,7 @@
|
|
|
449
449
|
"fs-extra": "^11.2.0",
|
|
450
450
|
"gray-matter": "^4.0.3",
|
|
451
451
|
"lucide-react": "^0.539.0",
|
|
452
|
+
"mermaid": "^11.0.0",
|
|
452
453
|
"next-intl": "^4.9.1",
|
|
453
454
|
"next-themes": "^0.4.6",
|
|
454
455
|
"ora": "^8.1.1",
|
|
@@ -469,7 +470,7 @@
|
|
|
469
470
|
"tailwind-merge": "^3.3.1",
|
|
470
471
|
"uuid": "^13.0.0",
|
|
471
472
|
"zod": "^4.1.5",
|
|
472
|
-
"@nextsparkjs/testing": "0.1.0-beta.
|
|
473
|
+
"@nextsparkjs/testing": "0.1.0-beta.178"
|
|
473
474
|
},
|
|
474
475
|
"scripts": {
|
|
475
476
|
"postinstall": "node scripts/postinstall.mjs || true",
|
package/styles/docs.css
CHANGED
|
@@ -80,3 +80,18 @@
|
|
|
80
80
|
.docs-content hr {
|
|
81
81
|
@apply my-8 border-border;
|
|
82
82
|
}
|
|
83
|
+
|
|
84
|
+
/* Mermaid diagrams: rendered client-side from a <pre class="mermaid"> marker */
|
|
85
|
+
.docs-content pre.mermaid {
|
|
86
|
+
@apply my-6 p-0 bg-transparent border-0 text-center overflow-x-auto;
|
|
87
|
+
line-height: normal;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.docs-content pre.mermaid svg {
|
|
91
|
+
@apply max-w-full h-auto;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/* Raw source shown briefly before the client render replaces it */
|
|
95
|
+
.docs-content pre.mermaid:not([data-processed="true"]) {
|
|
96
|
+
@apply text-sm text-muted-foreground;
|
|
97
|
+
}
|
|
@@ -2,6 +2,8 @@ import type { Metadata } from 'next'
|
|
|
2
2
|
import { LoginForm } from '@nextsparkjs/core/components/auth/forms/LoginForm'
|
|
3
3
|
import { getTemplateOrDefault, getMetadataOrDefault } from '@nextsparkjs/core/lib/template-resolver'
|
|
4
4
|
|
|
5
|
+
export const dynamic = 'force-dynamic'
|
|
6
|
+
|
|
5
7
|
const defaultMetadata: Metadata = {
|
|
6
8
|
title: 'Sign In',
|
|
7
9
|
description: 'Sign in to your account to access the platform',
|
|
@@ -6,6 +6,10 @@ import { getTemplateOrDefault, getMetadataOrDefault } from '@nextsparkjs/core/li
|
|
|
6
6
|
import { AUTH_CONFIG } from '@nextsparkjs/core/lib/config'
|
|
7
7
|
import { TeamService } from '@nextsparkjs/core/lib/services'
|
|
8
8
|
|
|
9
|
+
// This page reads DB state (TeamService.hasGlobal) and redirect()s during render,
|
|
10
|
+
// so it must render per-request and never be statically prerendered at build time.
|
|
11
|
+
export const dynamic = 'force-dynamic'
|
|
12
|
+
|
|
9
13
|
const defaultMetadata: Metadata = {
|
|
10
14
|
title: 'Create Account',
|
|
11
15
|
description: 'Create your account to start using our platform',
|