@lowdefy/server-dev 5.6.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{pages/_app.js → client/App.jsx} +21 -33
- package/client/DevStreamContext.js +30 -0
- package/client/Inspector.jsx +226 -0
- package/{lib/client/Page.js → client/Page.jsx} +4 -8
- package/client/Reload.jsx +85 -0
- package/client/Routing.jsx +117 -0
- package/client/feedback/FeedbackMount.jsx +83 -0
- package/client/feedback/FeedbackOverlay.jsx +901 -0
- package/client/feedback/captureTabScreenshot.js +84 -0
- package/client/feedback/copyToClipboard.js +45 -0
- package/client/feedback/drawAnnotationsSvg.js +119 -0
- package/client/feedback/elementInspect.js +99 -0
- package/client/feedback/feedbackStyles.js +301 -0
- package/client/feedback/pinResponsiveImages.js +59 -0
- package/client/feedback/sendFeedback.js +39 -0
- package/client/feedback/useFeedbackToggle.js +70 -0
- package/client/main.jsx +42 -0
- package/client/openInEditor/OpenInEditorListener.jsx +268 -0
- package/client/openInEditor/findBlockLocation.js +51 -0
- package/lib/build/app.js +8 -1
- package/lib/build/appMeta.js +8 -1
- package/lib/build/auth.js +8 -1
- package/lib/build/config.js +8 -1
- package/lib/build/i18n.js +8 -1
- package/lib/build/logger.js +8 -1
- package/lib/build/theme.js +8 -1
- package/lib/client/auth/{Auth.js → Auth.jsx} +11 -2
- package/lib/client/auth/{AuthConfigured.js → AuthConfigured.jsx} +23 -10
- package/lib/client/setPageId.js +6 -5
- package/lib/client/utils/useMutateCache.js +14 -1
- package/lib/client/utils/usePageConfig.js +45 -43
- package/lib/client/utils/usePageConfig.test.mjs +62 -0
- package/lib/docs/captureAnnotatedScreenshot.js +88 -0
- package/lib/docs/checkpointPaths.js +91 -0
- package/lib/docs/checkpointStore.js +240 -0
- package/lib/docs/checkpointToMocks.js +65 -0
- package/lib/docs/clientErrorStore.js +36 -0
- package/lib/docs/configCheckpoints.test.mjs +156 -0
- package/lib/docs/createConfigCheckpoint.js +92 -0
- package/lib/docs/createDocsMcpServer.js +531 -0
- package/lib/docs/createDocsMcpServer.test.mjs +141 -0
- package/lib/docs/devMockRegistry.js +68 -0
- package/lib/docs/docs.test.mjs +448 -0
- package/lib/docs/enrichFeedback.js +71 -0
- package/lib/docs/evalOperator.js +47 -0
- package/lib/docs/evalOperator.test.mjs +157 -0
- package/lib/docs/evalOperatorHeadless.js +87 -0
- package/lib/docs/evalOperatorHeadless.test.mjs +72 -0
- package/lib/docs/evalOperatorInTab.js +48 -0
- package/lib/docs/findConfig.js +260 -0
- package/lib/docs/formatFeedback.js +121 -0
- package/lib/docs/formatFeedback.test.mjs +204 -0
- package/lib/docs/getAppMap.js +180 -0
- package/lib/docs/getBrowser.js +127 -0
- package/lib/docs/getBrowser.test.mjs +122 -0
- package/lib/docs/getBuildStatus.js +37 -0
- package/lib/docs/getCoreDoc.js +63 -0
- package/lib/docs/getDocsManifest.js +40 -0
- package/lib/docs/getExamples.js +58 -0
- package/lib/docs/getOverview.js +115 -0
- package/lib/docs/getPageConfig.js +53 -0
- package/lib/docs/getPluginDoc.js +55 -0
- package/lib/docs/getSchema.js +65 -0
- package/lib/docs/inspectState.js +47 -0
- package/lib/docs/inspectState.test.mjs +140 -0
- package/lib/docs/inspectStateFromTab.js +40 -0
- package/lib/docs/inspectStateHeadless.js +84 -0
- package/lib/docs/inspectStateHeadless.test.mjs +60 -0
- package/lib/docs/isWriteRequestsAllowed.js +50 -0
- package/lib/docs/listConfigCheckpoints.js +55 -0
- package/lib/docs/listPlugins.js +80 -0
- package/lib/docs/listTypes.js +105 -0
- package/lib/docs/loadState.js +152 -0
- package/lib/docs/mapPageBuildErrors.js +28 -0
- package/lib/docs/normalizeTypeKind.js +40 -0
- package/{pages/api/endpoints/[...endpointId].js → lib/docs/readBuildArtifact.js} +15 -12
- package/lib/docs/readPageArtifact.js +26 -0
- package/lib/docs/resolvePluginDir.js +49 -0
- package/lib/docs/resolveSource.js +48 -0
- package/lib/docs/resolveSource.test.mjs +100 -0
- package/lib/docs/revertConfigCheckpoint.js +73 -0
- package/lib/docs/runRequest.js +141 -0
- package/lib/docs/saveAnnotatedScreenshot.js +43 -0
- package/lib/docs/scaffoldPage.js +75 -0
- package/lib/docs/scaffoldPage.test.mjs +97 -0
- package/lib/docs/screenshotPage.js +110 -0
- package/lib/docs/screenshotPage.test.mjs +59 -0
- package/lib/docs/screenshotPageClip.test.mjs +102 -0
- package/lib/docs/searchDocs.js +67 -0
- package/lib/docs/setupTestFixtures.mjs +335 -0
- package/lib/docs/snapshotState.js +59 -0
- package/lib/docs/stateCheckpoints.test.mjs +366 -0
- package/lib/docs/tabAvailable.js +28 -0
- package/lib/docs/tabChannel.js +119 -0
- package/lib/docs/tabChannel.test.mjs +130 -0
- package/lib/server/auth/{getAuthOptions.js → getAuthConfig.js} +7 -4
- package/lib/server/auth/getDevSession.js +70 -0
- package/lib/server/auth/getDevSession.test.mjs +138 -0
- package/lib/server/auth/getHeadlessUser.js +40 -0
- package/lib/server/auth/{getMockSession.js → getMockUser.js} +8 -21
- package/lib/server/auth/headlessUser.js +29 -0
- package/lib/server/auth/resolveHeadlessUser.js +38 -0
- package/lib/server/auth/resolveHeadlessUser.test.mjs +78 -0
- package/lib/server/auth/session.js +39 -0
- package/lib/server/auth/strategies.js +47 -0
- package/lib/server/createLowdefyContext.js +122 -0
- package/lib/server/getPageJitEnrichment.test.mjs +105 -0
- package/lib/server/jitPageBuilder.js +125 -12
- package/lib/server/log/createHandleError.test.mjs +126 -0
- package/manager/getContext.mjs +3 -7
- package/manager/processes/initialBuild.mjs +0 -2
- package/manager/processes/installPlugins.mjs +19 -11
- package/manager/processes/lowdefyBuild.mjs +29 -7
- package/manager/processes/lowdefyBuild.test.mjs +102 -0
- package/manager/processes/readDotEnv.mjs +13 -1
- package/manager/processes/shutdownServer.mjs +6 -6
- package/manager/processes/startProxy.mjs +160 -0
- package/manager/processes/startProxy.test.mjs +102 -0
- package/manager/processes/startServer.mjs +33 -16
- package/manager/processes/startWatchers.mjs +2 -2
- package/manager/processes/warnAuthUrlPortMismatch.mjs +47 -0
- package/manager/processes/warnAuthUrlPortMismatch.test.mjs +93 -0
- package/manager/run.mjs +50 -0
- package/manager/utils/createCustomPluginMessagesMap.mjs +8 -1
- package/manager/utils/createCustomPluginTypesMap.mjs +1 -0
- package/manager/utils/formatNoticeBox.mjs +30 -0
- package/manager/utils/{getNextBin.mjs → getViteBin.mjs} +7 -10
- package/manager/utils/updatePageTailwindCss.mjs +7 -1
- package/manager/utils/writeBuildStatus.mjs +39 -0
- package/manager/utils/writeBuildStatus.test.mjs +76 -0
- package/manager/watchers/lowdefyBuildWatcher.mjs +0 -1
- package/manager/watchers/moduleBuildWatcher.mjs +0 -1
- package/manager/watchers/{nextBuildWatcher.mjs → serverArtifactWatcher.mjs} +18 -20
- package/package.json +61 -47
- package/package.original.json +64 -50
- package/postcss.config.cjs +1 -0
- package/src/app.js +175 -0
- package/src/html/renderDevPage.js +86 -0
- package/src/lib/getPathSegments.js +25 -0
- package/src/lib/safeScriptJson.js +38 -0
- package/src/middleware/apiContext.js +32 -0
- package/src/middleware/errorHandler.js +48 -0
- package/src/middleware/errorHandler.test.mjs +235 -0
- package/src/routes/agent.js +64 -0
- package/src/routes/apiPage.js +37 -0
- package/src/routes/auth.js +49 -0
- package/src/routes/auth.test.mjs +84 -0
- package/src/routes/clientError.js +57 -0
- package/src/routes/cron.js +47 -0
- package/src/routes/cronForward.js +47 -0
- package/src/routes/detached.js +47 -0
- package/src/routes/devInspect.js +111 -0
- package/src/routes/devInspect.test.mjs +119 -0
- package/{pages/[[...pageId]].js → src/routes/devTools.js} +4 -2
- package/{pages/404.js → src/routes/docs/appMap.js} +6 -2
- package/{pages/api/dev-tools.js → src/routes/docs/buildStatus.js} +5 -4
- package/src/routes/docs/checkpointsCreate.js +28 -0
- package/src/routes/docs/checkpointsList.js +23 -0
- package/{pages/api/icons/dynamic.js → src/routes/docs/checkpointsRevert.js} +11 -12
- package/src/routes/docs/content.js +31 -0
- package/src/routes/docs/evalOperator.js +56 -0
- package/src/routes/docs/examples.js +33 -0
- package/src/routes/docs/find.js +30 -0
- package/src/routes/docs/index.js +23 -0
- package/src/routes/docs/inspectState.js +44 -0
- package/src/routes/docs/loadState.js +49 -0
- package/src/routes/docs/mcp.js +30 -0
- package/src/routes/docs/pageConfig.js +36 -0
- package/src/routes/docs/parseUserParam.js +52 -0
- package/src/routes/docs/parseUserParam.test.mjs +45 -0
- package/src/routes/docs/pluginDoc.js +33 -0
- package/src/routes/docs/plugins.js +23 -0
- package/src/routes/docs/runRequest.js +36 -0
- package/{lib/server/auth/getServerSession.js → src/routes/docs/schema.js} +17 -15
- package/src/routes/docs/screenshot.js +72 -0
- package/src/routes/docs/search.js +27 -0
- package/src/routes/docs/snapshotState.js +36 -0
- package/src/routes/docs/stateCheckpointsList.js +26 -0
- package/src/routes/docs/types.js +33 -0
- package/src/routes/endpoints.js +71 -0
- package/src/routes/feedback.js +110 -0
- package/src/routes/feedback.test.mjs +204 -0
- package/src/routes/jitPage.js +86 -0
- package/src/routes/mcp.js +34 -0
- package/{pages/api → src/routes}/ping.js +4 -2
- package/src/routes/reload.js +79 -0
- package/src/routes/request.js +74 -0
- package/src/routes/request.test.mjs +124 -0
- package/{pages/api → src/routes}/root.js +4 -4
- package/src/routes/usage.js +46 -0
- package/src/routes/websocket.js +33 -0
- package/src/websocket/devWebSocket.js +78 -0
- package/vite.config.js +111 -0
- package/.eslintrc.yaml +0 -1
- package/lib/client/App.js +0 -80
- package/lib/client/Reload.js +0 -60
- package/lib/server/apiWrapper.js +0 -109
- package/lib/server/compileCss.js +0 -39
- package/manager/processes/compileCss.mjs +0 -54
- package/manager/processes/nextBuild.mjs +0 -50
- package/next.config.js +0 -25
- package/pages/_document.js +0 -109
- package/pages/api/agent/[...path].js +0 -86
- package/pages/api/auth/[...nextauth].js +0 -48
- package/pages/api/client-error.js +0 -45
- package/pages/api/js/[env].js +0 -42
- package/pages/api/page/[...pageId].js +0 -74
- package/pages/api/reload.js +0 -53
- package/pages/api/request/[...path].js +0 -41
- /package/lib/client/{BuildErrorPage.js → BuildErrorPage.jsx} +0 -0
- /package/lib/client/{BuildingPage.js → BuildingPage.jsx} +0 -0
- /package/lib/client/{ErrorBar.js → ErrorBar.jsx} +0 -0
- /package/lib/client/{InstallingPluginsPage.js → InstallingPluginsPage.jsx} +0 -0
- /package/lib/client/{RestartingPage.js → RestartingPage.jsx} +0 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// html-to-image embeds img.src and clones srcset/<source> attributes as-is:
|
|
18
|
+
// the selection the browser actually displays (img.currentSrc) is ignored, so
|
|
19
|
+
// a <picture> rasterizes its fallback src stretched into the displayed box
|
|
20
|
+
// (e.g. the PageHeaderMenu square logo warped into the wordmark's slot), and
|
|
21
|
+
// remote srcset candidates can't load inside the sandboxed SVG rasterization
|
|
22
|
+
// anyway. Pin every responsive image to its currentSrc for the capture — the
|
|
23
|
+
// URL the tab already displays, so nothing changes visually — and return a
|
|
24
|
+
// restore function for the caller's finally block.
|
|
25
|
+
function pinResponsiveImages() {
|
|
26
|
+
const restores = [];
|
|
27
|
+
|
|
28
|
+
document.querySelectorAll('img').forEach((img) => {
|
|
29
|
+
const isResponsive =
|
|
30
|
+
img.hasAttribute('srcset') || (img.currentSrc && img.currentSrc !== img.src);
|
|
31
|
+
if (!img.currentSrc || !isResponsive) return;
|
|
32
|
+
const src = img.getAttribute('src');
|
|
33
|
+
const srcset = img.getAttribute('srcset');
|
|
34
|
+
restores.push(() => {
|
|
35
|
+
if (src === null) {
|
|
36
|
+
img.removeAttribute('src');
|
|
37
|
+
} else {
|
|
38
|
+
img.setAttribute('src', src);
|
|
39
|
+
}
|
|
40
|
+
if (srcset !== null) {
|
|
41
|
+
img.setAttribute('srcset', srcset);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
img.setAttribute('src', img.currentSrc);
|
|
45
|
+
img.removeAttribute('srcset');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
document.querySelectorAll('picture > source[srcset]').forEach((source) => {
|
|
49
|
+
const srcset = source.getAttribute('srcset');
|
|
50
|
+
restores.push(() => source.setAttribute('srcset', srcset));
|
|
51
|
+
source.removeAttribute('srcset');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
return function restore() {
|
|
55
|
+
restores.forEach((fn) => fn());
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default pinResponsiveImages;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Sends the batch to the dev server for enrichment (blockId → yaml file:line
|
|
18
|
+
// via the build's keyMap) and gets back the formatted, agent-readable text.
|
|
19
|
+
// Best-effort, matching Inspector.jsx's postResult style — a dev-only feature
|
|
20
|
+
// must never throw into the app it rides along with. Returns the formatted
|
|
21
|
+
// string, or null on any failure.
|
|
22
|
+
async function sendFeedback({ basePath, batch }) {
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(`${basePath}/lowdefy-feedback`, {
|
|
25
|
+
method: 'POST',
|
|
26
|
+
headers: { 'Content-Type': 'application/json' },
|
|
27
|
+
body: JSON.stringify(batch),
|
|
28
|
+
});
|
|
29
|
+
if (!response.ok) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
const data = await response.json();
|
|
33
|
+
return typeof data.formatted === 'string' ? data.formatted : null;
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export default sendFeedback;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { useEffect, useRef } from 'react';
|
|
18
|
+
|
|
19
|
+
import { tinykeys } from 'tinykeys';
|
|
20
|
+
|
|
21
|
+
// Binds the global Cmd/Ctrl+/ feedback toggle, matching the app's own
|
|
22
|
+
// shortcut convention (see createShortcutManager.js): tinykeys on window,
|
|
23
|
+
// "$mod" for the platform modifier. Registered at capture phase with
|
|
24
|
+
// stopImmediatePropagation so the app's own shortcut manager (or any block
|
|
25
|
+
// event bound to the same key) never also fires.
|
|
26
|
+
function useFeedbackToggle({ onToggle }) {
|
|
27
|
+
const onToggleRef = useRef(onToggle);
|
|
28
|
+
onToggleRef.current = onToggle;
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (typeof window === 'undefined') {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
let unsubscribe;
|
|
36
|
+
try {
|
|
37
|
+
unsubscribe = tinykeys(
|
|
38
|
+
window,
|
|
39
|
+
{
|
|
40
|
+
'$mod+Slash': (event) => {
|
|
41
|
+
try {
|
|
42
|
+
event.preventDefault();
|
|
43
|
+
event.stopImmediatePropagation();
|
|
44
|
+
if (onToggleRef.current) {
|
|
45
|
+
onToggleRef.current();
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// Never let the toggle handler crash the app.
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{ capture: true }
|
|
53
|
+
);
|
|
54
|
+
} catch {
|
|
55
|
+
// tinykeys unavailable or binding failed — the feature just won't toggle.
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return () => {
|
|
59
|
+
try {
|
|
60
|
+
if (unsubscribe) {
|
|
61
|
+
unsubscribe();
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
// no-op
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}, []);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export default useFeedbackToggle;
|
package/client/main.jsx
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// CSS layer order — MUST be the first CSS import. This locks the cascade
|
|
18
|
+
// priority (antd > base/preflight) before antd's StyleProvider injects
|
|
19
|
+
// @layer antd {} at runtime. Vite's PostCSS pipeline compiles globals.css
|
|
20
|
+
// (Tailwind) on the fly and hot-replaces it — the old tailwind-jit.css
|
|
21
|
+
// side-channel is gone.
|
|
22
|
+
import '../build/layer-order.css';
|
|
23
|
+
|
|
24
|
+
import React from 'react';
|
|
25
|
+
import { createRoot } from 'react-dom/client';
|
|
26
|
+
import createRouter from '@lowdefy/client/adapters/createRouter.js';
|
|
27
|
+
|
|
28
|
+
import App from './App.jsx';
|
|
29
|
+
|
|
30
|
+
import '../build/globals.css';
|
|
31
|
+
|
|
32
|
+
const config = JSON.parse(document.getElementById('__LOWDEFY_CONFIG__').textContent);
|
|
33
|
+
const router = createRouter({ basePath: config.basePath ?? '', window });
|
|
34
|
+
const container = document.getElementById('root');
|
|
35
|
+
|
|
36
|
+
// Keep one React root across Vite HMR updates.
|
|
37
|
+
const root = import.meta.hot?.data.root ?? createRoot(container);
|
|
38
|
+
if (import.meta.hot) {
|
|
39
|
+
import.meta.hot.data.root = root;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
root.render(<App config={config} router={router} />);
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
18
|
+
import { createPortal } from 'react-dom';
|
|
19
|
+
|
|
20
|
+
import { blockAncestorChain, nearestBlock } from '../feedback/elementInspect.js';
|
|
21
|
+
import { highlightBox, labelChip, overlayContainer } from '../feedback/feedbackStyles.js';
|
|
22
|
+
import findBlockLocation from './findBlockLocation.js';
|
|
23
|
+
|
|
24
|
+
const STYLE_TAG_ID = 'lowdefy-open-in-editor-style-tag';
|
|
25
|
+
const BODY_CLASS = 'lowdefy-open-in-editor-target';
|
|
26
|
+
|
|
27
|
+
// Cursor over arbitrary app descendants can't be forced from inline styles —
|
|
28
|
+
// same trick as feedbackStyles' picking/drawing classes.
|
|
29
|
+
function injectStyleTag() {
|
|
30
|
+
if (typeof document === 'undefined' || document.getElementById(STYLE_TAG_ID)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const style = document.createElement('style');
|
|
34
|
+
style.id = STYLE_TAG_ID;
|
|
35
|
+
style.textContent = `
|
|
36
|
+
.${BODY_CLASS}, .${BODY_CLASS} * {
|
|
37
|
+
cursor: pointer !important;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
document.head.appendChild(style);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function removeStyleTag() {
|
|
44
|
+
if (typeof document === 'undefined') {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
document.getElementById(STYLE_TAG_ID)?.remove();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// The feedback overlay owns clicks while it is open (its picking mode claims
|
|
51
|
+
// them) — the Option/Alt+click shortcut stays out of the way until it closes.
|
|
52
|
+
// :not() skips this component's own highlight portal, which carries
|
|
53
|
+
// data-lowdefy-feedback purely for screenshot/picking exclusion.
|
|
54
|
+
function feedbackOverlayOpen() {
|
|
55
|
+
return Boolean(
|
|
56
|
+
document.querySelector('[data-lowdefy-feedback]:not([data-lowdefy-open-in-editor])')
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Option (macOS) / Alt (Windows, Linux) alone is the shortcut. Cmd/Ctrl are left
|
|
61
|
+
// to the browser (open link in new tab) and Shift to text selection, so any of
|
|
62
|
+
// them held alongside means the developer wants the browser's behaviour.
|
|
63
|
+
function isOpenModifier(event) {
|
|
64
|
+
return event.altKey && !event.metaKey && !event.ctrlKey && !event.shiftKey;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Dev-only: Option/Alt+click any element to open the YAML that defines its
|
|
68
|
+
// block in VS Code (vscode://file deep link, at the exact line). While the
|
|
69
|
+
// modifier is held, the hovered block shows the same blue highlight box and
|
|
70
|
+
// blockId chip as the annotation overlay's picking mode, and the cursor
|
|
71
|
+
// switches to a pointer — so the developer sees exactly what a click will
|
|
72
|
+
// open. Mounted always-on from Routing.jsx alongside Inspector and
|
|
73
|
+
// FeedbackMount, and follows their resilience contract — every handler is
|
|
74
|
+
// try/caught so this can never take the app down with it.
|
|
75
|
+
//
|
|
76
|
+
// The click resolves the nearest block and, when that id is generated at
|
|
77
|
+
// runtime (list items, Dynamic endpoints), falls back through the ancestor
|
|
78
|
+
// block chain until an id resolves in config — the same strategy the
|
|
79
|
+
// feedback pipeline uses in enrichFeedback.js.
|
|
80
|
+
function OpenInEditorListener({ basePath, pageId }) {
|
|
81
|
+
const [hoverBlock, setHoverBlock] = useState(null); // { blockId, rect }
|
|
82
|
+
// Refs keep the freshest values without re-binding the capture listeners
|
|
83
|
+
// on every SPA navigation or hover change.
|
|
84
|
+
const pageIdRef = useRef(pageId);
|
|
85
|
+
pageIdRef.current = pageId;
|
|
86
|
+
const hoverRef = useRef(hoverBlock);
|
|
87
|
+
hoverRef.current = hoverBlock;
|
|
88
|
+
const lastPointerRef = useRef(null); // { x, y }
|
|
89
|
+
|
|
90
|
+
// Style tag lifecycle — the listener is always mounted, so this lives for
|
|
91
|
+
// the app's lifetime and only the body class toggles per hover.
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
injectStyleTag();
|
|
94
|
+
return () => removeStyleTag();
|
|
95
|
+
}, []);
|
|
96
|
+
|
|
97
|
+
// Pointer cursor only while a block is highlighted — over non-block areas
|
|
98
|
+
// the cursor stays native, signalling the click would do nothing.
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
try {
|
|
101
|
+
document.body.classList.toggle(BODY_CLASS, Boolean(hoverBlock));
|
|
102
|
+
} catch {
|
|
103
|
+
// no-op
|
|
104
|
+
}
|
|
105
|
+
return () => {
|
|
106
|
+
try {
|
|
107
|
+
document.body.classList.remove(BODY_CLASS);
|
|
108
|
+
} catch {
|
|
109
|
+
// no-op
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}, [hoverBlock]);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
function clearHover() {
|
|
116
|
+
if (hoverRef.current) {
|
|
117
|
+
setHoverBlock(null);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function resolveHover() {
|
|
122
|
+
try {
|
|
123
|
+
const point = lastPointerRef.current;
|
|
124
|
+
if (!point || feedbackOverlayOpen()) {
|
|
125
|
+
clearHover();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
const el = document.elementFromPoint(point.x, point.y);
|
|
129
|
+
const blockId = nearestBlock(el);
|
|
130
|
+
const blockEl = blockId ? document.getElementById(`bl-${blockId}`) : null;
|
|
131
|
+
if (!blockEl) {
|
|
132
|
+
clearHover();
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const rect = blockEl.getBoundingClientRect();
|
|
136
|
+
setHoverBlock({
|
|
137
|
+
blockId,
|
|
138
|
+
rect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },
|
|
139
|
+
});
|
|
140
|
+
} catch {
|
|
141
|
+
clearHover();
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function onPointerMove(event) {
|
|
146
|
+
try {
|
|
147
|
+
lastPointerRef.current = { x: event.clientX, y: event.clientY };
|
|
148
|
+
if (isOpenModifier(event)) {
|
|
149
|
+
resolveHover();
|
|
150
|
+
} else {
|
|
151
|
+
clearHover();
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
// Best-effort hover tracking.
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Modifier pressed/released while the mouse is stationary — pointermove
|
|
159
|
+
// alone would leave the highlight stale.
|
|
160
|
+
function onKeyDown(event) {
|
|
161
|
+
if (event.key === 'Alt') {
|
|
162
|
+
resolveHover();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function onKeyUp(event) {
|
|
167
|
+
if (event.key === 'Alt') {
|
|
168
|
+
clearHover();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Switching apps releases the modifier without a keyup reaching the page.
|
|
173
|
+
function onBlur() {
|
|
174
|
+
clearHover();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Keep the highlight glued to the block while the app scrolls under a
|
|
178
|
+
// stationary cursor.
|
|
179
|
+
function onScroll() {
|
|
180
|
+
if (hoverRef.current) {
|
|
181
|
+
resolveHover();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async function openBlockInEditor({ candidates }) {
|
|
186
|
+
for (const blockId of candidates) {
|
|
187
|
+
const location = await findBlockLocation({
|
|
188
|
+
basePath,
|
|
189
|
+
blockId,
|
|
190
|
+
pageId: pageIdRef.current,
|
|
191
|
+
});
|
|
192
|
+
if (location) {
|
|
193
|
+
window.location.assign(`vscode://file${location.file}:${location.line}`);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
// eslint-disable-next-line no-console
|
|
199
|
+
console.info(
|
|
200
|
+
`Lowdefy: no config location found for "${candidates[0]}" — the block is generated at runtime.`
|
|
201
|
+
);
|
|
202
|
+
} catch {
|
|
203
|
+
// no-op
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function onClickCapture(event) {
|
|
208
|
+
try {
|
|
209
|
+
if (!isOpenModifier(event)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
if (feedbackOverlayOpen()) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const candidates = blockAncestorChain(event.target);
|
|
216
|
+
if (candidates.length === 0) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
// Only claim the click once a block is under the cursor — plain
|
|
220
|
+
// Option/Alt+clicks elsewhere keep their default behaviour.
|
|
221
|
+
event.preventDefault();
|
|
222
|
+
event.stopPropagation();
|
|
223
|
+
void openBlockInEditor({ candidates });
|
|
224
|
+
} catch {
|
|
225
|
+
// Never let the shortcut break the developer's app.
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
window.addEventListener('pointermove', onPointerMove);
|
|
230
|
+
window.addEventListener('keydown', onKeyDown);
|
|
231
|
+
window.addEventListener('keyup', onKeyUp);
|
|
232
|
+
window.addEventListener('blur', onBlur);
|
|
233
|
+
window.addEventListener('scroll', onScroll, true);
|
|
234
|
+
window.addEventListener('click', onClickCapture, { capture: true });
|
|
235
|
+
return () => {
|
|
236
|
+
window.removeEventListener('pointermove', onPointerMove);
|
|
237
|
+
window.removeEventListener('keydown', onKeyDown);
|
|
238
|
+
window.removeEventListener('keyup', onKeyUp);
|
|
239
|
+
window.removeEventListener('blur', onBlur);
|
|
240
|
+
window.removeEventListener('scroll', onScroll, true);
|
|
241
|
+
window.removeEventListener('click', onClickCapture, { capture: true });
|
|
242
|
+
};
|
|
243
|
+
}, [basePath]);
|
|
244
|
+
|
|
245
|
+
if (!hoverBlock || typeof document === 'undefined' || !document.body) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
// data-lowdefy-feedback keeps this out of the annotation overlay's element
|
|
249
|
+
// picking and out of tab-captured screenshots (both exclude that root).
|
|
250
|
+
return createPortal(
|
|
251
|
+
<div data-lowdefy-feedback data-lowdefy-open-in-editor style={overlayContainer}>
|
|
252
|
+
<div
|
|
253
|
+
style={{
|
|
254
|
+
...highlightBox,
|
|
255
|
+
top: hoverBlock.rect.top,
|
|
256
|
+
left: hoverBlock.rect.left,
|
|
257
|
+
width: hoverBlock.rect.width,
|
|
258
|
+
height: hoverBlock.rect.height,
|
|
259
|
+
}}
|
|
260
|
+
>
|
|
261
|
+
<span style={labelChip}>{hoverBlock.blockId}</span>
|
|
262
|
+
</div>
|
|
263
|
+
</div>,
|
|
264
|
+
document.body
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export default OpenInEditorListener;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2020-2026 Lowdefy, Inc
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Resolves a blockId to its YAML source via the dev server's existing
|
|
18
|
+
// GET /lowdefy-docs/find/{id}?pageId= route (which JIT-builds the page's
|
|
19
|
+
// keyMap on demand). Returns { file, line } with an absolute file path, or
|
|
20
|
+
// null when the id has no configured location. Best-effort, matching
|
|
21
|
+
// sendFeedback.js — a dev-only feature must never throw into the app.
|
|
22
|
+
async function findBlockLocation({ basePath, blockId, pageId }) {
|
|
23
|
+
try {
|
|
24
|
+
// Omit pageId when unknown — an empty ?pageId= reads as an unknown page
|
|
25
|
+
// on the server, while no param falls back to scanning all built pages.
|
|
26
|
+
const query = pageId ? `?pageId=${encodeURIComponent(pageId)}` : '';
|
|
27
|
+
const response = await fetch(
|
|
28
|
+
`${basePath}/lowdefy-docs/find/${encodeURIComponent(blockId)}${query}`
|
|
29
|
+
);
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const data = await response.json();
|
|
34
|
+
const source = data.matches?.[0]?.location?.source;
|
|
35
|
+
if (typeof source !== 'string' || source.length === 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
// source is "<absolute path>" or "<absolute path>:<line>" — a numeric
|
|
39
|
+
// suffix after the last colon is the 1-based line number. Splitting on
|
|
40
|
+
// the last colon keeps Windows drive-letter colons intact.
|
|
41
|
+
const match = source.match(/^(.*):(\d+)$/);
|
|
42
|
+
if (match) {
|
|
43
|
+
return { file: match[1], line: Number(match[2]) };
|
|
44
|
+
}
|
|
45
|
+
return { file: source, line: 1 };
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default findBlockLocation;
|
package/lib/build/app.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/app.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/appMeta.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/appMeta.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/auth.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/auth.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/config.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/config.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/i18n.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/i18n.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/logger.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/logger.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
package/lib/build/theme.js
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
17
|
+
// Build artifacts are read from disk because the Hono server runs as
|
|
18
|
+
// unbundled Node.js ESM — JSON imports would need import attributes, and
|
|
19
|
+
// client code imports the build JSON directly through Vite instead.
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import path from 'node:path';
|
|
16
22
|
import { serializer } from '@lowdefy/helpers';
|
|
17
|
-
|
|
23
|
+
|
|
24
|
+
const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/theme.json'), 'utf8'));
|
|
18
25
|
|
|
19
26
|
export default serializer.deserialize(raw);
|
|
@@ -13,13 +13,22 @@
|
|
|
13
13
|
See the License for the specific language governing permissions and
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
|
+
|
|
16
17
|
/* eslint-disable react/jsx-props-no-spreading */
|
|
17
18
|
|
|
18
19
|
import React from 'react';
|
|
19
|
-
import AuthConfigured from './AuthConfigured.
|
|
20
|
+
import AuthConfigured from './AuthConfigured.jsx';
|
|
20
21
|
import AuthNotConfigured from './AuthNotConfigured.js';
|
|
21
22
|
|
|
22
|
-
import
|
|
23
|
+
import { serializer } from '@lowdefy/helpers';
|
|
24
|
+
|
|
25
|
+
// Client code imports the build JSON directly — Vite handles JSON imports;
|
|
26
|
+
// the lib/build/*.js wrappers are server-only (they read from disk).
|
|
27
|
+
// Deserialize to restore arrays from their ~arr build markers (providers
|
|
28
|
+
// must be a real array for single-provider inference in createAuthMethods).
|
|
29
|
+
import rawAuthConfig from '../../../build/auth.json';
|
|
30
|
+
|
|
31
|
+
const authConfig = serializer.deserialize(rawAuthConfig);
|
|
23
32
|
|
|
24
33
|
function Auth({ children, session }) {
|
|
25
34
|
if (authConfig.configured === true) {
|