@lowdefy/server-dev 0.0.0-experimental-20260720072521 → 0.0.0-experimental-20260721080548

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 (196) hide show
  1. package/.eslintrc.yaml +1 -0
  2. package/lib/build/app.js +1 -8
  3. package/lib/build/appMeta.js +1 -8
  4. package/lib/build/auth.js +1 -8
  5. package/lib/build/config.js +1 -8
  6. package/lib/build/i18n.js +1 -8
  7. package/lib/build/logger.js +1 -8
  8. package/lib/build/theme.js +1 -8
  9. package/lib/client/App.js +80 -0
  10. package/{client/Page.jsx → lib/client/Page.js} +5 -5
  11. package/{client/Reload.jsx → lib/client/Reload.js} +7 -5
  12. package/lib/client/auth/{Auth.jsx → Auth.js} +5 -12
  13. package/lib/client/auth/AuthConfigured.js +61 -0
  14. package/lib/client/auth/AuthNotConfigured.js +2 -22
  15. package/lib/client/setPageId.js +5 -6
  16. package/lib/client/utils/useMutateCache.js +1 -14
  17. package/lib/client/utils/usePageConfig.js +11 -31
  18. package/lib/server/apiWrapper.js +116 -0
  19. package/lib/server/auth/{getStrategies.js → getAuthOptions.js} +9 -14
  20. package/lib/server/auth/{getMockUser.js → getMockSession.js} +19 -10
  21. package/{src/routes/docs/checkpointsRevert.js → lib/server/auth/getServerSession.js} +13 -11
  22. package/lib/server/compileCss.js +39 -0
  23. package/lib/server/jitPageBuilder.js +11 -36
  24. package/lib/server/log/logRequest.js +3 -1
  25. package/manager/getContext.mjs +7 -3
  26. package/manager/processes/compileCss.mjs +54 -0
  27. package/manager/processes/initialBuild.mjs +2 -0
  28. package/manager/processes/installPlugins.mjs +11 -19
  29. package/manager/processes/lowdefyBuild.mjs +7 -29
  30. package/manager/processes/nextBuild.mjs +50 -0
  31. package/manager/processes/readDotEnv.mjs +1 -13
  32. package/manager/processes/shutdownServer.mjs +6 -6
  33. package/manager/processes/startServer.mjs +7 -8
  34. package/manager/processes/startWatchers.mjs +2 -2
  35. package/manager/run.mjs +0 -41
  36. package/manager/utils/createCustomPluginMessagesMap.mjs +1 -8
  37. package/manager/utils/createCustomPluginTypesMap.mjs +2 -3
  38. package/manager/utils/{getViteBin.mjs → getNextBin.mjs} +10 -7
  39. package/manager/utils/updatePageTailwindCss.mjs +1 -7
  40. package/manager/watchers/lowdefyBuildWatcher.mjs +1 -0
  41. package/manager/watchers/moduleBuildWatcher.mjs +1 -0
  42. package/manager/watchers/{serverArtifactWatcher.mjs → nextBuildWatcher.mjs} +22 -18
  43. package/next.config.js +25 -0
  44. package/package.json +47 -63
  45. package/package.original.json +55 -66
  46. package/{src/routes/devTools.js → pages/404.js} +2 -4
  47. package/{src/routes/docs/appMap.js → pages/[[...pageId]].js} +2 -6
  48. package/{client/App.jsx → pages/_app.js} +33 -21
  49. package/pages/_document.js +107 -0
  50. package/pages/api/agent/[...path].js +86 -0
  51. package/pages/api/auth/[...nextauth].js +48 -0
  52. package/pages/api/client-error.js +45 -0
  53. package/{src/routes/docs/buildStatus.js → pages/api/dev-tools.js} +4 -5
  54. package/{lib/docs/clientErrorStore.js → pages/api/endpoints/[...endpointId].js} +11 -15
  55. package/{src/lib/serveBuildJs.js → pages/api/icons/dynamic.js} +12 -8
  56. package/{src/routes/jsEnv.js → pages/api/js/[env].js} +20 -8
  57. package/pages/api/page/[...pageId].js +74 -0
  58. package/{src/routes → pages/api}/ping.js +2 -4
  59. package/pages/api/reload.js +53 -0
  60. package/pages/api/request/[...path].js +41 -0
  61. package/{src/routes → pages/api}/root.js +4 -4
  62. package/client/Inspector.jsx +0 -206
  63. package/client/Routing.jsx +0 -106
  64. package/client/feedback/FeedbackMount.jsx +0 -83
  65. package/client/feedback/FeedbackOverlay.jsx +0 -901
  66. package/client/feedback/captureTabScreenshot.js +0 -84
  67. package/client/feedback/copyToClipboard.js +0 -45
  68. package/client/feedback/drawAnnotationsSvg.js +0 -119
  69. package/client/feedback/elementInspect.js +0 -99
  70. package/client/feedback/feedbackStyles.js +0 -301
  71. package/client/feedback/pinResponsiveImages.js +0 -59
  72. package/client/feedback/sendFeedback.js +0 -39
  73. package/client/feedback/useFeedbackToggle.js +0 -70
  74. package/client/main.jsx +0 -42
  75. package/client/openInEditor/OpenInEditorListener.jsx +0 -261
  76. package/client/openInEditor/findBlockLocation.js +0 -51
  77. package/lib/client/auth/AuthConfigured.jsx +0 -155
  78. package/lib/docs/captureAnnotatedScreenshot.js +0 -88
  79. package/lib/docs/checkpointPaths.js +0 -91
  80. package/lib/docs/checkpointStore.js +0 -234
  81. package/lib/docs/checkpointToMocks.js +0 -65
  82. package/lib/docs/configCheckpoints.test.mjs +0 -156
  83. package/lib/docs/createConfigCheckpoint.js +0 -92
  84. package/lib/docs/createDocsMcpServer.js +0 -506
  85. package/lib/docs/createDocsMcpServer.test.mjs +0 -123
  86. package/lib/docs/devMockRegistry.js +0 -68
  87. package/lib/docs/docs.test.mjs +0 -448
  88. package/lib/docs/enrichFeedback.js +0 -71
  89. package/lib/docs/evalOperator.js +0 -39
  90. package/lib/docs/evalOperator.test.mjs +0 -112
  91. package/lib/docs/evalOperatorHeadless.js +0 -87
  92. package/lib/docs/evalOperatorHeadless.test.mjs +0 -72
  93. package/lib/docs/evalOperatorInTab.js +0 -48
  94. package/lib/docs/findConfig.js +0 -260
  95. package/lib/docs/formatFeedback.js +0 -121
  96. package/lib/docs/formatFeedback.test.mjs +0 -204
  97. package/lib/docs/getAppMap.js +0 -180
  98. package/lib/docs/getBrowser.js +0 -115
  99. package/lib/docs/getBuildStatus.js +0 -37
  100. package/lib/docs/getCoreDoc.js +0 -63
  101. package/lib/docs/getDocsManifest.js +0 -40
  102. package/lib/docs/getExamples.js +0 -58
  103. package/lib/docs/getOverview.js +0 -115
  104. package/lib/docs/getPageConfig.js +0 -53
  105. package/lib/docs/getPluginDoc.js +0 -55
  106. package/lib/docs/getSchema.js +0 -65
  107. package/lib/docs/inspectState.js +0 -41
  108. package/lib/docs/inspectState.test.mjs +0 -91
  109. package/lib/docs/inspectStateFromTab.js +0 -40
  110. package/lib/docs/inspectStateHeadless.js +0 -84
  111. package/lib/docs/inspectStateHeadless.test.mjs +0 -60
  112. package/lib/docs/isWriteRequestsAllowed.js +0 -50
  113. package/lib/docs/listConfigCheckpoints.js +0 -55
  114. package/lib/docs/listPlugins.js +0 -80
  115. package/lib/docs/listTypes.js +0 -105
  116. package/lib/docs/loadState.js +0 -139
  117. package/lib/docs/mapPageBuildErrors.js +0 -28
  118. package/lib/docs/normalizeTypeKind.js +0 -40
  119. package/lib/docs/readBuildArtifact.js +0 -35
  120. package/lib/docs/readPageArtifact.js +0 -26
  121. package/lib/docs/resolvePluginDir.js +0 -49
  122. package/lib/docs/revertConfigCheckpoint.js +0 -73
  123. package/lib/docs/runRequest.js +0 -141
  124. package/lib/docs/saveAnnotatedScreenshot.js +0 -43
  125. package/lib/docs/scaffoldPage.js +0 -75
  126. package/lib/docs/scaffoldPage.test.mjs +0 -97
  127. package/lib/docs/screenshotPage.js +0 -109
  128. package/lib/docs/screenshotPage.test.mjs +0 -59
  129. package/lib/docs/screenshotPageClip.test.mjs +0 -102
  130. package/lib/docs/searchDocs.js +0 -67
  131. package/lib/docs/setupTestFixtures.mjs +0 -335
  132. package/lib/docs/snapshotState.js +0 -59
  133. package/lib/docs/stateCheckpoints.test.mjs +0 -348
  134. package/lib/docs/tabAvailable.js +0 -28
  135. package/lib/docs/tabChannel.js +0 -119
  136. package/lib/docs/tabChannel.test.mjs +0 -130
  137. package/lib/server/auth/createSystemContext.js +0 -70
  138. package/lib/server/auth/getAuth.js +0 -46
  139. package/lib/server/auth/getHeadlessUser.js +0 -39
  140. package/lib/server/auth/headlessUser.js +0 -30
  141. package/lib/server/createLowdefyContext.js +0 -133
  142. package/lib/server/loadDynamicJsMap.js +0 -47
  143. package/manager/processes/lowdefyBuild.test.mjs +0 -102
  144. package/manager/utils/formatNoticeBox.mjs +0 -30
  145. package/manager/utils/writeBuildStatus.mjs +0 -39
  146. package/manager/utils/writeBuildStatus.test.mjs +0 -76
  147. package/postcss.config.cjs +0 -1
  148. package/src/app.js +0 -188
  149. package/src/html/renderDevPage.js +0 -86
  150. package/src/lib/getPathSegments.js +0 -25
  151. package/src/lib/safeScriptJson.js +0 -38
  152. package/src/middleware/apiContext.js +0 -32
  153. package/src/middleware/errorHandler.js +0 -55
  154. package/src/routes/agent.js +0 -64
  155. package/src/routes/auth.js +0 -43
  156. package/src/routes/clientError.js +0 -57
  157. package/src/routes/cron.js +0 -44
  158. package/src/routes/detached.js +0 -51
  159. package/src/routes/devInspect.js +0 -94
  160. package/src/routes/docs/checkpointsCreate.js +0 -28
  161. package/src/routes/docs/checkpointsList.js +0 -23
  162. package/src/routes/docs/content.js +0 -31
  163. package/src/routes/docs/evalOperator.js +0 -46
  164. package/src/routes/docs/examples.js +0 -33
  165. package/src/routes/docs/find.js +0 -30
  166. package/src/routes/docs/index.js +0 -23
  167. package/src/routes/docs/inspectState.js +0 -34
  168. package/src/routes/docs/loadState.js +0 -38
  169. package/src/routes/docs/mcp.js +0 -30
  170. package/src/routes/docs/pageConfig.js +0 -36
  171. package/src/routes/docs/pluginDoc.js +0 -33
  172. package/src/routes/docs/plugins.js +0 -23
  173. package/src/routes/docs/runRequest.js +0 -34
  174. package/src/routes/docs/schema.js +0 -37
  175. package/src/routes/docs/screenshot.js +0 -66
  176. package/src/routes/docs/search.js +0 -27
  177. package/src/routes/docs/snapshotState.js +0 -36
  178. package/src/routes/docs/stateCheckpointsList.js +0 -26
  179. package/src/routes/docs/types.js +0 -33
  180. package/src/routes/endpoints.js +0 -76
  181. package/src/routes/feedback.js +0 -110
  182. package/src/routes/feedback.test.mjs +0 -204
  183. package/src/routes/iconsDynamic.js +0 -25
  184. package/src/routes/jitPage.js +0 -99
  185. package/src/routes/reload.js +0 -81
  186. package/src/routes/request.js +0 -66
  187. package/src/routes/usage.js +0 -46
  188. package/src/routes/user.js +0 -27
  189. package/src/routes/websocket.js +0 -33
  190. package/src/websocket/devWebSocket.js +0 -78
  191. package/vite.config.js +0 -111
  192. /package/lib/client/{BuildErrorPage.jsx → BuildErrorPage.js} +0 -0
  193. /package/lib/client/{BuildingPage.jsx → BuildingPage.js} +0 -0
  194. /package/lib/client/{ErrorBar.jsx → ErrorBar.js} +0 -0
  195. /package/lib/client/{InstallingPluginsPage.jsx → InstallingPluginsPage.js} +0 -0
  196. /package/lib/client/{RestartingPage.jsx → RestartingPage.js} +0 -0
package/.eslintrc.yaml ADDED
@@ -0,0 +1 @@
1
+ extends: 'plugin:@next/next/core-web-vitals'
package/lib/build/app.js CHANGED
@@ -13,14 +13,7 @@
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';
22
16
  import { serializer } from '@lowdefy/helpers';
23
-
24
- const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/app.json'), 'utf8'));
17
+ import raw from '../../build/app.json';
25
18
 
26
19
  export default serializer.deserialize(raw);
@@ -13,14 +13,7 @@
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';
22
16
  import { serializer } from '@lowdefy/helpers';
23
-
24
- const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/appMeta.json'), 'utf8'));
17
+ import raw from '../../build/appMeta.json';
25
18
 
26
19
  export default serializer.deserialize(raw);
package/lib/build/auth.js CHANGED
@@ -13,14 +13,7 @@
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';
22
16
  import { serializer } from '@lowdefy/helpers';
23
-
24
- const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/auth.json'), 'utf8'));
17
+ import raw from '../../build/auth.json';
25
18
 
26
19
  export default serializer.deserialize(raw);
@@ -13,14 +13,7 @@
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';
22
16
  import { serializer } from '@lowdefy/helpers';
23
-
24
- const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/config.json'), 'utf8'));
17
+ import raw from '../../build/config.json';
25
18
 
26
19
  export default serializer.deserialize(raw);
package/lib/build/i18n.js CHANGED
@@ -13,14 +13,7 @@
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';
22
16
  import { serializer } from '@lowdefy/helpers';
23
-
24
- const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/i18n.json'), 'utf8'));
17
+ import raw from '../../build/i18n.json';
25
18
 
26
19
  export default serializer.deserialize(raw);
@@ -13,14 +13,7 @@
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';
22
16
  import { serializer } from '@lowdefy/helpers';
23
-
24
- const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/logger.json'), 'utf8'));
17
+ import raw from '../../build/logger.json';
25
18
 
26
19
  export default serializer.deserialize(raw);
@@ -13,14 +13,7 @@
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';
22
16
  import { serializer } from '@lowdefy/helpers';
23
-
24
- const raw = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'build/theme.json'), 'utf8'));
17
+ import raw from '../../build/theme.json';
25
18
 
26
19
  export default serializer.deserialize(raw);
@@ -0,0 +1,80 @@
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, { Suspense } from 'react';
18
+
19
+ import { useRouter } from 'next/router';
20
+
21
+ import Head from 'next/head';
22
+ import Link from 'next/link';
23
+
24
+ import BuildingPage from './BuildingPage.js';
25
+ import Reload from './Reload.js';
26
+ import Page from './Page.js';
27
+ import setPageId from './setPageId.js';
28
+ import { getReloadVersion } from './utils/useMutateCache.js';
29
+ import useRootConfig from './utils/useRootConfig.js';
30
+
31
+ import actions from '../../build/plugins/actions.js';
32
+ import blockMetas from '../../build/plugins/blockMetas.json';
33
+ import blocks from '../../build/plugins/blocks.js';
34
+ import icons from '../../build/plugins/icons.js';
35
+ import operators from '../../build/plugins/operators/client.js';
36
+ import staticJsMap from '../../build/plugins/operators/clientJsMap.js';
37
+
38
+ const App = ({ auth, lowdefy }) => {
39
+ const router = useRouter();
40
+ const { data: rootConfig } = useRootConfig(router.basePath);
41
+
42
+ if (rootConfig?.theme) {
43
+ lowdefy.theme = rootConfig.theme;
44
+ }
45
+
46
+ const { redirect, pageId } = setPageId(router, rootConfig);
47
+ if (redirect) {
48
+ router.push(`/${pageId}`);
49
+ return '';
50
+ }
51
+ return (
52
+ <Reload basePath={router.basePath} lowdefy={lowdefy}>
53
+ {(resetContext) => (
54
+ <Suspense key={`${pageId}_${getReloadVersion()}`} fallback={<BuildingPage />}>
55
+ <Page
56
+ auth={auth}
57
+ Components={{ Head, Link }}
58
+ config={{
59
+ rootConfig,
60
+ }}
61
+ jsMap={staticJsMap}
62
+ lowdefy={lowdefy}
63
+ pageId={pageId}
64
+ resetContext={resetContext}
65
+ router={router}
66
+ types={{
67
+ actions,
68
+ blockMetas,
69
+ blocks,
70
+ icons,
71
+ operators,
72
+ }}
73
+ />
74
+ </Suspense>
75
+ )}
76
+ </Reload>
77
+ );
78
+ };
79
+
80
+ export default App;
@@ -18,10 +18,10 @@ import React, { useEffect, useRef } from 'react';
18
18
  import { GenIcon } from 'react-icons/lib';
19
19
  import Client from '@lowdefy/client';
20
20
 
21
- import BuildErrorPage from '../lib/client/BuildErrorPage.jsx';
22
- import InstallingPluginsPage from '../lib/client/InstallingPluginsPage.jsx';
23
- import RestartingPage from '../lib/client/RestartingPage.jsx';
24
- import usePageConfig from '../lib/client/utils/usePageConfig.js';
21
+ import BuildErrorPage from './BuildErrorPage.js';
22
+ import InstallingPluginsPage from './InstallingPluginsPage.js';
23
+ import RestartingPage from './RestartingPage.js';
24
+ import usePageConfig from './utils/usePageConfig.js';
25
25
 
26
26
  const Page = ({
27
27
  auth,
@@ -48,7 +48,7 @@ const Page = ({
48
48
  }, [pageConfig?._warnings, lowdefy]);
49
49
 
50
50
  if (!pageConfig) {
51
- router.replace({ pathname: '/404' });
51
+ router.replace(`/404`);
52
52
  return '';
53
53
  }
54
54
  if (pageConfig.buildError) {
@@ -16,12 +16,9 @@
16
16
 
17
17
  import React, { useEffect, useState } from 'react';
18
18
 
19
- import useMutateCache from '../lib/client/utils/useMutateCache.js';
20
- import waitForRestartedServer from '../lib/client/utils/waitForRestartedServer.js';
19
+ import useMutateCache from './utils/useMutateCache.js';
20
+ import waitForRestartedServer from './utils/waitForRestartedServer.js';
21
21
 
22
- // SSE listener — config rebuilds notify via /api/reload. Tailwind CSS updates
23
- // arrive through Vite HMR now (globals.css is in the dev module graph), so
24
- // the old tailwind-jit.css link cache-bust is gone.
25
22
  const Reload = ({ children, basePath, lowdefy }) => {
26
23
  const [reset, setReset] = useState(false);
27
24
  const [restarting, setRestarting] = useState(false);
@@ -37,6 +34,11 @@ const Reload = ({ children, basePath, lowdefy }) => {
37
34
  if (lowdefy._internal?.initialised) {
38
35
  lowdefy._internal.initialised = false;
39
36
  }
37
+ // Refresh JIT CSS link to pick up newly compiled Tailwind classes
38
+ const cssLink = document.getElementById('tailwind-jit-css');
39
+ if (cssLink) {
40
+ cssLink.href = `${basePath}/tailwind-jit.css?v=${Date.now()}`;
41
+ }
40
42
  setReset(true);
41
43
  console.log('Reloaded config.');
42
44
  }, 600);
@@ -13,25 +13,18 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
+ /* eslint-disable react/jsx-props-no-spreading */
16
17
 
17
18
  import React from 'react';
18
- import AuthConfigured from './AuthConfigured.jsx';
19
+ import AuthConfigured from './AuthConfigured.js';
19
20
  import AuthNotConfigured from './AuthNotConfigured.js';
20
21
 
21
- import { serializer } from '@lowdefy/helpers';
22
+ import authConfig from '../../build/auth.js';
22
23
 
23
- // Client code imports the build JSON directly — Vite handles JSON imports;
24
- // the lib/build/*.js wrappers are server-only (they read from disk).
25
- // Deserialize to restore arrays from their ~arr build markers (providers
26
- // must be a real array for provider lookups in createAuthMethods).
27
- import rawAuthConfig from '../../../build/auth.json';
28
-
29
- const authConfig = serializer.deserialize(rawAuthConfig);
30
-
31
- function Auth({ children, user }) {
24
+ function Auth({ children, session }) {
32
25
  if (authConfig.configured === true) {
33
26
  return (
34
- <AuthConfigured serverUser={user} authConfig={authConfig}>
27
+ <AuthConfigured serverSession={session} authConfig={authConfig}>
35
28
  {(auth) => children(auth)}
36
29
  </AuthConfigured>
37
30
  );
@@ -0,0 +1,61 @@
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
+ /* eslint-disable react/jsx-props-no-spreading */
17
+
18
+ import React, { useEffect, useRef } from 'react';
19
+ import { getSession, SessionProvider, signIn, signOut, useSession } from 'next-auth/react';
20
+
21
+ import lowdefyConfig from '../../build/config.js';
22
+
23
+ function Session({ children }) {
24
+ const wasAuthenticated = useRef(false);
25
+ const { data: session, status } = useSession();
26
+ wasAuthenticated.current = wasAuthenticated.current || status === 'authenticated';
27
+
28
+ useEffect(() => {
29
+ if (wasAuthenticated.current && status === 'unauthenticated') {
30
+ window.location.reload();
31
+ }
32
+ }, [status]);
33
+
34
+ // If session is passed to SessionProvider from getServerSideProps
35
+ // we won't have a loading state here.
36
+ // But 404 uses getStaticProps so we have this for 404.
37
+ if (status === 'loading') {
38
+ return '';
39
+ }
40
+ return children(session);
41
+ }
42
+
43
+ function AuthConfigured({ authConfig, children, serverSession }) {
44
+ const auth = { authConfig, getSession, signIn, signOut };
45
+ let basePath = lowdefyConfig.basePath;
46
+ if (basePath) {
47
+ basePath = `${basePath}/api/auth`;
48
+ }
49
+ return (
50
+ <SessionProvider session={serverSession} basePath={basePath}>
51
+ <Session>
52
+ {(session) => {
53
+ auth.session = session;
54
+ return children(auth);
55
+ }}
56
+ </Session>
57
+ </SessionProvider>
58
+ );
59
+ }
60
+
61
+ export default AuthConfigured;
@@ -13,6 +13,7 @@
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
+ /* eslint-disable react/jsx-props-no-spreading */
16
17
 
17
18
  function authNotConfigured() {
18
19
  throw new Error('Auth not configured.');
@@ -21,30 +22,9 @@ function authNotConfigured() {
21
22
  function AuthNotConfigured({ authConfig, children }) {
22
23
  const auth = {
23
24
  authConfig,
24
- user: null,
25
- acceptInvitation: authNotConfigured,
26
- addPasskey: authNotConfigured,
27
- changePassword: authNotConfigured,
28
- deletePasskey: authNotConfigured,
29
- getResolvedUser: authNotConfigured,
30
25
  getSession: authNotConfigured,
31
- impersonateUser: authNotConfigured,
32
- requestPasswordReset: authNotConfigured,
33
- resetPassword: authNotConfigured,
34
- revokeOtherSessions: authNotConfigured,
35
- sendVerificationEmail: authNotConfigured,
36
- setActiveOrganization: authNotConfigured,
37
- signInEmail: authNotConfigured,
38
- signInMagicLink: authNotConfigured,
39
- signInOauth2: authNotConfigured,
40
- signInSocial: authNotConfigured,
26
+ signIn: authNotConfigured,
41
27
  signOut: authNotConfigured,
42
- signUpEmail: authNotConfigured,
43
- stopImpersonating: authNotConfigured,
44
- twoFactorDisable: authNotConfigured,
45
- twoFactorEnable: authNotConfigured,
46
- twoFactorVerifyBackupCode: authNotConfigured,
47
- twoFactorVerifyTotp: authNotConfigured,
48
28
  };
49
29
 
50
30
  return children(auth);
@@ -14,19 +14,18 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- // location comes from the custom router (@lowdefy/client/adapters):
18
- // { pageId, pathname, search }. pageId is null at the root path.
19
- function setPageId(location, rootConfig) {
20
- if (location.pageId === '404') {
17
+ function setPageId(router, rootConfig) {
18
+ if (router.pathname === `/404`) {
21
19
  return { redirect: false, pageId: '404' };
22
20
  }
23
- if (!location.pageId) {
21
+ const segments = router.query.pageId;
22
+ if (!segments || segments.length === 0) {
24
23
  if (rootConfig.home.configured === false) {
25
24
  return { redirect: true, pageId: rootConfig.home.pageId };
26
25
  }
27
26
  return { redirect: false, pageId: rootConfig.home.pageId };
28
27
  }
29
- return { redirect: false, pageId: location.pageId };
28
+ return { redirect: false, pageId: segments.join('/') };
30
29
  }
31
30
 
32
31
  export default setPageId;
@@ -16,25 +16,12 @@
16
16
 
17
17
  import { useSWRConfig } from 'swr';
18
18
 
19
- // Module-level counters that bust usePageConfig SWR keys: reloadVersion moves
20
- // on config reloads (all pages), navVersion on client-side navigations
21
- // (dynamic pages only — server-resolved content must re-resolve per
22
- // navigation, never serve from the SWR cache).
23
19
  let reloadVersion = 0;
24
- let navVersion = 0;
25
20
 
26
21
  function getReloadVersion() {
27
22
  return reloadVersion;
28
23
  }
29
24
 
30
- function bumpNavVersion() {
31
- navVersion += 1;
32
- }
33
-
34
- function getNavVersion() {
35
- return navVersion;
36
- }
37
-
38
25
  function useMutateCache(basePath) {
39
26
  const { mutate } = useSWRConfig();
40
27
  return () => {
@@ -47,5 +34,5 @@ function useMutateCache(basePath) {
47
34
  };
48
35
  }
49
36
 
50
- export { bumpNavVersion, getNavVersion, getReloadVersion };
37
+ export { getReloadVersion };
51
38
  export default useMutateCache;
@@ -13,12 +13,7 @@
13
13
 
14
14
  import useSWR from 'swr';
15
15
 
16
- import { getNavVersion, getReloadVersion } from './useMutateCache.js';
17
-
18
- // URLs whose config is server-resolved per request — learned from the fetched
19
- // config's dynamic flag, so the first visit caches like a static page and
20
- // every later navigation refetches.
21
- const dynamicUrls = new Set();
16
+ import { getReloadVersion } from './useMutateCache.js';
22
17
 
23
18
  function parseJsModule(text) {
24
19
  const fn = new Function('exports', text.replace('export default', 'exports.default ='));
@@ -54,13 +49,6 @@ async function fetchPageConfig(url) {
54
49
  if (res.status === 404) {
55
50
  return null;
56
51
  }
57
- if (res.status === 401) {
58
- // Logged-out navigation to a protected page - full load to the login
59
- // page so it can return here after sign-in.
60
- const { redirect } = await res.json();
61
- window.location.assign(redirect ?? '/404');
62
- return new Promise(() => {});
63
- }
64
52
  const data = await res.json();
65
53
  if (data?.buildError) {
66
54
  return data;
@@ -83,31 +71,23 @@ async function fetchPageConfig(url) {
83
71
  data._jsEntries = jsEntries;
84
72
  data._dynamicIcons = dynamicIcons;
85
73
 
86
- if (data?.dynamic === true) {
87
- dynamicUrls.add(url);
74
+ // Bust CSS cache so the browser picks up newly compiled Tailwind classes
75
+ const cssLink = document.getElementById('tailwind-jit-css');
76
+ if (cssLink) {
77
+ cssLink.href = `${basePath}/tailwind-jit.css?v=${Date.now()}`;
88
78
  }
89
79
 
90
80
  return data;
91
81
  }
92
82
 
93
83
  function usePageConfig(pageId, basePath) {
94
- // Forward the current query string so server-side Dynamic block resolution
95
- // sees the same urlQuery as an initial HTML load. Including it in the SWR
96
- // key also caches dynamic pages per query string.
97
- const url = `${basePath}/api/page/${pageId}${window.location.search}`;
84
+ const url = `${basePath}/api/page/${pageId}`;
98
85
  // Include reloadVersion in the SWR key so that after a config reload,
99
- // previously cached page data is not reused. Dynamic pages also key on the
100
- // navigation version — server-resolved content must re-resolve on every
101
- // navigation, never serve from the SWR cache. The fetcher receives
102
- // [url, ...versions] but only uses url — the versions just bust the cache.
103
- const navVersion = dynamicUrls.has(url) ? getNavVersion() : 0;
104
- const { data } = useSWR(
105
- [url, getReloadVersion(), navVersion],
106
- ([fetchUrl]) => fetchPageConfig(fetchUrl),
107
- {
108
- suspense: true,
109
- }
110
- );
86
+ // previously cached page data is not reused. The fetcher receives
87
+ // [url, version] but only uses url — the version just busts the cache.
88
+ const { data } = useSWR([url, getReloadVersion()], ([fetchUrl]) => fetchPageConfig(fetchUrl), {
89
+ suspense: true,
90
+ });
111
91
  return { data };
112
92
  }
113
93
 
@@ -0,0 +1,116 @@
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
+ import fs from 'fs';
17
+ import path from 'path';
18
+ import { createApiContext } from '@lowdefy/api';
19
+ import { getSecretsFromEnv } from '@lowdefy/node-utils';
20
+ import { serializer } from '@lowdefy/helpers';
21
+ import { v4 as uuid } from 'uuid';
22
+
23
+ import agents from '../../build/plugins/agents.js';
24
+ import appMeta from '../build/appMeta.js';
25
+ import config from '../build/config.js';
26
+ import i18nConfig from '../build/i18n.js';
27
+ import connections from '../../build/plugins/connections.js';
28
+ import createLogger from './log/createLogger.js';
29
+ import fileCache from './fileCache.js';
30
+ import getServerSession from './auth/getServerSession.js';
31
+ import createHandleError from './log/createHandleError.js';
32
+ import logRequest from './log/logRequest.js';
33
+ import operators from '../../build/plugins/operators/server.js';
34
+ import staticJsMap from '../../build/plugins/operators/serverJsMap.js';
35
+ import getAuthOptions from './auth/getAuthOptions.js';
36
+
37
+ const secrets = getSecretsFromEnv();
38
+
39
+ // Dynamic JS map loading for JIT-built pages
40
+ let cachedJsMapMtime = null;
41
+ let cachedJsMap = staticJsMap;
42
+
43
+ function loadDynamicJsMap(buildDirectory) {
44
+ const jsMapPath = path.join(buildDirectory, 'plugins', 'operators', 'serverJsMap.js');
45
+ try {
46
+ const stat = fs.statSync(jsMapPath);
47
+ if (cachedJsMapMtime && stat.mtimeMs === cachedJsMapMtime) {
48
+ return cachedJsMap;
49
+ }
50
+ cachedJsMapMtime = stat.mtimeMs;
51
+ // For server-side, we can read and eval the JS file
52
+ const content = fs.readFileSync(jsMapPath, 'utf8');
53
+ const fn = new Function('exports', content.replace('export default', 'exports.default ='));
54
+ const exports = {};
55
+ fn(exports);
56
+ cachedJsMap = { ...staticJsMap, ...(exports.default ?? {}) };
57
+ return cachedJsMap;
58
+ } catch {
59
+ return cachedJsMap;
60
+ }
61
+ }
62
+
63
+ function apiWrapper(handler) {
64
+ return async function wrappedHandler(req, res) {
65
+ const buildDirectory = path.join(process.cwd(), 'build');
66
+ const jsMap = loadDynamicJsMap(buildDirectory);
67
+
68
+ const context = {
69
+ // Important to give absolute path so Next can trace build files
70
+ rid: uuid(),
71
+ agents,
72
+ appMeta,
73
+ buildDirectory,
74
+ configDirectory: process.env.LOWDEFY_DIRECTORY_CONFIG || process.cwd(),
75
+ config,
76
+ connections,
77
+ fileCache,
78
+ headers: req?.headers,
79
+ i18n: i18nConfig,
80
+ jsMap,
81
+ handleError: async (err) => {
82
+ console.error(err);
83
+ },
84
+ logger: console,
85
+ operators,
86
+ req,
87
+ res,
88
+ secrets,
89
+ };
90
+ try {
91
+ context.logger = createLogger();
92
+ context.handleError = createHandleError({ context });
93
+ context.authOptions = getAuthOptions(context);
94
+ if (!req.url.startsWith('/api/auth')) {
95
+ context.session = await getServerSession(context);
96
+ }
97
+ createApiContext(context);
98
+ logRequest({ context });
99
+ // Await here so that if handler throws it is caught.
100
+ const response = await handler({ context, req, res });
101
+ // TODO: Log response time?
102
+ return response;
103
+ } catch (error) {
104
+ await context.handleError(error);
105
+ const serialized = serializer.serialize(error);
106
+ if (serialized?.['~e']) {
107
+ delete serialized['~e'].received;
108
+ delete serialized['~e'].stack;
109
+ delete serialized['~e'].configKey;
110
+ }
111
+ res.status(500).json(serialized);
112
+ }
113
+ };
114
+ }
115
+
116
+ export default apiWrapper;