@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.
Files changed (42) hide show
  1. package/dist/components/devtools/MarkdownViewer.d.ts.map +1 -1
  2. package/dist/components/devtools/MarkdownViewer.js +5 -0
  3. package/dist/components/devtools/api-explorer/ApiDocsModal.d.ts.map +1 -1
  4. package/dist/components/devtools/api-explorer/ApiDocsModal.js +0 -0
  5. package/dist/components/devtools/api-explorer/ApiEndpointsSidebar.d.ts.map +1 -1
  6. package/dist/components/devtools/api-explorer/ApiEndpointsSidebar.js +16 -5
  7. package/dist/components/devtools/api-explorer/ApiExplorer.d.ts.map +1 -1
  8. package/dist/components/devtools/api-explorer/ApiExplorer.js +63 -21
  9. package/dist/components/devtools/api-explorer/ApiRequestPanel.d.ts +3 -1
  10. package/dist/components/devtools/api-explorer/ApiRequestPanel.d.ts.map +1 -1
  11. package/dist/components/devtools/api-explorer/ApiRequestPanel.js +5 -1
  12. package/dist/components/devtools/api-explorer/explorer-storage.d.ts +12 -0
  13. package/dist/components/devtools/api-explorer/explorer-storage.d.ts.map +1 -0
  14. package/dist/components/devtools/api-explorer/explorer-storage.js +25 -0
  15. package/dist/components/devtools/api-tester/AuthSelector.d.ts +4 -1
  16. package/dist/components/devtools/api-tester/AuthSelector.d.ts.map +1 -1
  17. package/dist/components/devtools/api-tester/AuthSelector.js +24 -2
  18. package/dist/components/docs/docs-content.d.ts.map +1 -1
  19. package/dist/components/docs/docs-content.js +17 -0
  20. package/dist/components/docs/mermaid-diagram.d.ts +11 -0
  21. package/dist/components/docs/mermaid-diagram.d.ts.map +1 -0
  22. package/dist/components/docs/mermaid-diagram.js +20 -0
  23. package/dist/hooks/useApiExplorerNavigation.d.ts.map +1 -1
  24. package/dist/hooks/useApiExplorerNavigation.js +5 -1
  25. package/dist/lib/api/auth/dual-auth.d.ts +14 -0
  26. package/dist/lib/api/auth/dual-auth.d.ts.map +1 -1
  27. package/dist/lib/api/auth/dual-auth.js +43 -2
  28. package/dist/lib/docs/mermaid.d.ts +15 -0
  29. package/dist/lib/docs/mermaid.d.ts.map +1 -0
  30. package/dist/lib/docs/mermaid.js +42 -0
  31. package/dist/lib/docs/parser.d.ts.map +1 -1
  32. package/dist/lib/docs/parser.js +2 -1
  33. package/dist/lib/docs/remark-mermaid.d.ts +15 -0
  34. package/dist/lib/docs/remark-mermaid.d.ts.map +1 -0
  35. package/dist/lib/docs/remark-mermaid.js +26 -0
  36. package/dist/styles/classes.json +3 -2
  37. package/dist/templates/app/(auth)/login/page.tsx +2 -0
  38. package/dist/templates/app/(auth)/signup/page.tsx +4 -0
  39. package/package.json +3 -2
  40. package/styles/docs.css +15 -0
  41. package/templates/app/(auth)/login/page.tsx +2 -0
  42. 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":"AAQA,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,2CA2CzE"}
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":"AAeA,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"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ApiEndpointsSidebar.d.ts","sourceRoot":"","sources":["../../../../src/components/devtools/api-explorer/ApiEndpointsSidebar.tsx"],"names":[],"mappings":"AAWA,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,2CAoP1B"}
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
- /* @__PURE__ */ new Set(["core", "entity", "theme", "plugin"])
48
- );
49
- const [expandedPrefixes, setExpandedPrefixes] = useState(/* @__PURE__ */ new Set());
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;AAY5F,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,2CAwXxE"}
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
- setPathParams(extractPathParams(path));
70
- setQueryParams(createEmptyRows());
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
- setPathParams(extractPathParams(urlPath));
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
- setPathParams(extractPathParams(urlPath));
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
- setPathParams(extractPathParams(initialEndpoint.path));
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
- setPathParams(extractPathParams(firstRoute.path));
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,2CA8NtB"}
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;CAC9C;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,MAAM,EACN,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,YAAY,GACb,EAAE,iBAAiB,2CAmEnB"}
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":"AAEA,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,gBAAgB,2CAQrD"}
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,CAwDhC"}
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
- router.push(newUrl);
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;AAO7D,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;CAC7B;AAqBD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBvF;AAqFD;;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"}
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;AAWH,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,CAgBD"}
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"}
@@ -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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
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
+ };
@@ -1,6 +1,6 @@
1
1
  {
2
- "generated": "2026-06-30T17:54:54.134Z",
3
- "totalClasses": 1081,
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.177",
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.177"
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',