@lowdefy/server-dev 0.0.0-experimental-20260608135213 → 0.0.0-experimental-20260610101734

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 (72) hide show
  1. package/{pages/_app.js → client/App.jsx} +16 -32
  2. package/{lib/client/Page.js → client/Page.jsx} +5 -5
  3. package/{lib/client/Reload.js → client/Reload.jsx} +5 -7
  4. package/{lib/client/App.js → client/Routing.jsx} +34 -23
  5. package/client/main.jsx +42 -0
  6. package/lib/build/app.js +8 -1
  7. package/lib/build/appMeta.js +8 -1
  8. package/lib/build/auth.js +8 -1
  9. package/lib/build/config.js +8 -1
  10. package/lib/build/i18n.js +8 -1
  11. package/lib/build/logger.js +8 -1
  12. package/lib/build/theme.js +8 -1
  13. package/lib/client/auth/{Auth.js → Auth.jsx} +11 -2
  14. package/lib/client/auth/{AuthConfigured.js → AuthConfigured.jsx} +23 -10
  15. package/lib/client/setPageId.js +6 -5
  16. package/lib/client/utils/usePageConfig.js +0 -5
  17. package/lib/server/auth/{getAuthOptions.js → getAuthConfig.js} +6 -4
  18. package/lib/server/auth/{getServerSession.js → session.js} +9 -8
  19. package/lib/server/jitPageBuilder.js +17 -7
  20. package/lib/server/readBuildApiArtifacts.mjs +53 -0
  21. package/lib/server/readBuildApiArtifacts.test.mjs +106 -0
  22. package/manager/getContext.mjs +2 -6
  23. package/manager/processes/initialBuild.mjs +0 -2
  24. package/manager/processes/shutdownServer.mjs +6 -6
  25. package/manager/processes/startServer.mjs +7 -7
  26. package/manager/processes/startWatchers.mjs +2 -2
  27. package/manager/run.mjs +10 -0
  28. package/manager/utils/{getNextBin.mjs → getViteBin.mjs} +7 -10
  29. package/manager/utils/updatePageTailwindCss.mjs +7 -1
  30. package/manager/watchers/lowdefyBuildWatcher.mjs +0 -1
  31. package/manager/watchers/moduleBuildWatcher.mjs +0 -1
  32. package/manager/watchers/{nextBuildWatcher.mjs → serverArtifactWatcher.mjs} +18 -20
  33. package/package.json +47 -41
  34. package/package.original.json +50 -44
  35. package/postcss.config.cjs +1 -0
  36. package/src/app.js +93 -0
  37. package/src/html/renderDevPage.js +86 -0
  38. package/src/lib/getPathSegments.js +25 -0
  39. package/src/lib/safeScriptJson.js +38 -0
  40. package/{pages/404.js → src/lib/serveBuildJs.js} +13 -2
  41. package/{lib/server/apiWrapper.js → src/middleware/apiContext.js} +35 -42
  42. package/src/middleware/errorHandler.js +44 -0
  43. package/src/routes/agent.js +64 -0
  44. package/src/routes/apiPage.js +35 -0
  45. package/src/routes/auth.js +38 -0
  46. package/{pages/api/client-error.js → src/routes/clientError.js} +13 -15
  47. package/{pages/[[...pageId]].js → src/routes/devTools.js} +4 -2
  48. package/{pages/api/endpoints/[...endpointId].js → src/routes/endpoints.js} +8 -7
  49. package/{pages/api/dev-tools.js → src/routes/iconsDynamic.js} +7 -4
  50. package/{pages/api/page/[...pageId].js → src/routes/jitPage.js} +28 -22
  51. package/src/routes/jsEnv.js +30 -0
  52. package/{pages/api → src/routes}/ping.js +4 -2
  53. package/src/routes/reload.js +54 -0
  54. package/{pages/api/request/[...path].js → src/routes/request.js} +11 -14
  55. package/{pages/api → src/routes}/root.js +4 -4
  56. package/src/routes/usage.js +48 -0
  57. package/vite.config.js +58 -0
  58. package/lib/server/compileCss.js +0 -39
  59. package/manager/processes/compileCss.mjs +0 -54
  60. package/manager/processes/nextBuild.mjs +0 -50
  61. package/next.config.js +0 -25
  62. package/pages/_document.js +0 -107
  63. package/pages/api/agent/[...path].js +0 -86
  64. package/pages/api/auth/[...nextauth].js +0 -48
  65. package/pages/api/icons/dynamic.js +0 -34
  66. package/pages/api/js/[env].js +0 -42
  67. package/pages/api/reload.js +0 -53
  68. /package/lib/client/{BuildErrorPage.js → BuildErrorPage.jsx} +0 -0
  69. /package/lib/client/{BuildingPage.js → BuildingPage.jsx} +0 -0
  70. /package/lib/client/{ErrorBar.js → ErrorBar.jsx} +0 -0
  71. /package/lib/client/{InstallingPluginsPage.js → InstallingPluginsPage.jsx} +0 -0
  72. /package/lib/client/{RestartingPage.js → RestartingPage.jsx} +0 -0
@@ -14,14 +14,7 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- // CSS layer order — MUST be the first CSS import. Turbopack treats this as critical
18
- // CSS that loads before hydration, locking the cascade priority (antd > base/preflight)
19
- // before antd's StyleProvider injects @layer antd {} at runtime.
20
- import '../build/layer-order.css';
21
-
22
17
  import React, { Suspense, useCallback, useEffect, useRef, useState } from 'react';
23
- import dynamic from 'next/dynamic';
24
- import { useRouter } from 'next/router';
25
18
  import useSWR from 'swr';
26
19
 
27
20
  import { ErrorBoundary } from '@lowdefy/block-utils';
@@ -33,13 +26,10 @@ import { XProvider } from '@ant-design/x';
33
26
  import antdLocaleLoaders from '../build/i18n/antdLocales.js';
34
27
  import antdXLocaleLoaders from '../build/i18n/antdXLocales.js';
35
28
  import dayjsLocaleMap from '../build/i18n/dayjsLocales.js';
36
- import Auth from '../lib/client/auth/Auth.js';
37
- import ErrorBar from '../lib/client/ErrorBar.js';
29
+ import Auth from '../lib/client/auth/Auth.jsx';
30
+ import ErrorBar from '../lib/client/ErrorBar.jsx';
38
31
  import request from '../lib/client/utils/request.js';
39
-
40
- // Full Tailwind CSS — also loaded via <link href="tailwind-jit.css"> in _document.js
41
- // for hot-reloading. The Turbopack chunk provides layer ordering guarantee on initial load.
42
- import '../build/globals.css';
32
+ import Routing from './Routing.jsx';
43
33
 
44
34
  function ThemeTokenResolver({ lowdefyRef, children }) {
45
35
  const { token } = antdTheme.useToken();
@@ -50,12 +40,11 @@ function ThemeTokenResolver({ lowdefyRef, children }) {
50
40
  return children;
51
41
  }
52
42
 
53
- function App({ Component }) {
54
- const router = useRouter();
43
+ function App({ config, router }) {
55
44
  const lowdefyRef = useRef({});
56
45
  const [runtimeErrors, setRuntimeErrors] = useState([]);
57
- // Subscribe to rootConfig SWR cache — deduplicates with inner App.js fetch.
58
- // Without suspense so _app.js doesn't suspend — just re-renders when data arrives.
46
+ // Subscribe to rootConfig SWR cache — deduplicates with Routing's fetch.
47
+ // Without suspense so App doesn't suspend — just re-renders when data arrives.
59
48
  const { data: rootConfig } = useSWR(`${router.basePath}/api/root`, (url) => request({ url }));
60
49
  if (rootConfig?.theme) {
61
50
  lowdefyRef.current.theme = rootConfig.theme;
@@ -99,12 +88,10 @@ function App({ Component }) {
99
88
  ]);
100
89
  }, []);
101
90
 
102
- // Clear runtime errors on route change
91
+ // Clear runtime errors on navigation
103
92
  useEffect(() => {
104
- const clearErrors = () => setRuntimeErrors([]);
105
- router.events.on('routeChangeStart', clearErrors);
106
- return () => router.events.off('routeChangeStart', clearErrors);
107
- }, [router.events]);
93
+ return router.subscribe(() => setRuntimeErrors([]));
94
+ }, [router]);
108
95
 
109
96
  const handleError = useCallback((error) => {
110
97
  if (lowdefyRef.current?._internal?.handleError) {
@@ -115,10 +102,11 @@ function App({ Component }) {
115
102
  }, []);
116
103
 
117
104
  // XProvider extends antd's ConfigProvider; merging antd + antd-X locale packs
118
- // gives X components their built-in strings alongside antd's.
119
- const mergedLocale = antdLocale || antdXLocale
120
- ? { ...(antdLocale ?? {}), ...(antdXLocale ?? {}) }
121
- : undefined;
105
+ // gives X components (Bubble, Sender, Conversations, ...) their built-in strings
106
+ // alongside antd's. antd X ships only en_US + zh_CN; other locales fall back
107
+ // to en_US for X-native strings.
108
+ const mergedLocale =
109
+ antdLocale || antdXLocale ? { ...(antdLocale ?? {}), ...(antdXLocale ?? {}) } : undefined;
122
110
 
123
111
  return (
124
112
  <StyleProvider layer>
@@ -153,7 +141,7 @@ function App({ Component }) {
153
141
  >
154
142
  <Auth>
155
143
  {(auth) => {
156
- return <Component auth={auth} lowdefy={lowdefyRef.current} />;
144
+ return <Routing auth={auth} lowdefy={lowdefyRef.current} router={router} />;
157
145
  }}
158
146
  </Auth>
159
147
  </Suspense>
@@ -166,8 +154,4 @@ function App({ Component }) {
166
154
  );
167
155
  }
168
156
 
169
- const DynamicApp = dynamic(() => Promise.resolve(App), {
170
- ssr: false,
171
- });
172
-
173
- export default DynamicApp;
157
+ 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 './BuildErrorPage.js';
22
- import InstallingPluginsPage from './InstallingPluginsPage.js';
23
- import RestartingPage from './RestartingPage.js';
24
- import usePageConfig from './utils/usePageConfig.js';
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';
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(`/404`);
51
+ router.replace({ pathname: '/404' });
52
52
  return '';
53
53
  }
54
54
  if (pageConfig.buildError) {
@@ -16,9 +16,12 @@
16
16
 
17
17
  import React, { useEffect, useState } from 'react';
18
18
 
19
- import useMutateCache from './utils/useMutateCache.js';
20
- import waitForRestartedServer from './utils/waitForRestartedServer.js';
19
+ import useMutateCache from '../lib/client/utils/useMutateCache.js';
20
+ import waitForRestartedServer from '../lib/client/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.
22
25
  const Reload = ({ children, basePath, lowdefy }) => {
23
26
  const [reset, setReset] = useState(false);
24
27
  const [restarting, setRestarting] = useState(false);
@@ -34,11 +37,6 @@ const Reload = ({ children, basePath, lowdefy }) => {
34
37
  if (lowdefy._internal?.initialised) {
35
38
  lowdefy._internal.initialised = false;
36
39
  }
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
- }
42
40
  setReset(true);
43
41
  console.log('Reloaded config.');
44
42
  }, 600);
@@ -14,40 +14,51 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import React, { Suspense } from 'react';
17
+ import React, { Suspense, useEffect, useState } from 'react';
18
18
 
19
- import { useRouter } from 'next/router';
19
+ import Head from '@lowdefy/client/adapters/Head.js';
20
+ import createLinkComponent from '@lowdefy/client/adapters/Link.js';
20
21
 
21
- import Head from 'next/head';
22
- import Link from 'next/link';
22
+ import BuildingPage from '../lib/client/BuildingPage.jsx';
23
+ import Reload from './Reload.jsx';
24
+ import Page from './Page.jsx';
25
+ import setPageId from '../lib/client/setPageId.js';
26
+ import { getReloadVersion } from '../lib/client/utils/useMutateCache.js';
27
+ import useRootConfig from '../lib/client/utils/useRootConfig.js';
23
28
 
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';
29
+ import actions from '../build/plugins/actions.js';
30
+ import blockMetas from '../build/plugins/blockMetas.json';
31
+ import blocks from '../build/plugins/blocks.js';
32
+ import icons from '../build/plugins/icons.js';
33
+ import operators from '../build/plugins/operators/client.js';
34
+ import staticJsMap from '../build/plugins/operators/clientJsMap.js';
30
35
 
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();
36
+ // Replaces lib/client/App.js — page resolution driven by the custom router
37
+ // instead of next/router, everything else preserved.
38
+ function Routing({ auth, lowdefy, router }) {
40
39
  const { data: rootConfig } = useRootConfig(router.basePath);
40
+ const [location, setLocation] = useState(() => router.getLocation());
41
+
42
+ useEffect(() => {
43
+ return router.subscribe(setLocation);
44
+ }, [router]);
45
+
46
+ const [Link] = useState(() => createLinkComponent({ router }));
41
47
 
42
48
  if (rootConfig?.theme) {
43
49
  lowdefy.theme = rootConfig.theme;
44
50
  }
45
51
 
46
- const { redirect, pageId } = setPageId(router, rootConfig);
52
+ const { redirect, pageId } = setPageId(location, rootConfig);
53
+ useEffect(() => {
54
+ if (redirect) {
55
+ router.replace({ pathname: `/${pageId}` });
56
+ }
57
+ }, [redirect, pageId, router]);
47
58
  if (redirect) {
48
- router.push(`/${pageId}`);
49
59
  return '';
50
60
  }
61
+
51
62
  return (
52
63
  <Reload basePath={router.basePath} lowdefy={lowdefy}>
53
64
  {(resetContext) => (
@@ -75,6 +86,6 @@ const App = ({ auth, lowdefy }) => {
75
86
  )}
76
87
  </Reload>
77
88
  );
78
- };
89
+ }
79
90
 
80
- export default App;
91
+ export default Routing;
@@ -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} />);
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
- import raw from '../../build/app.json';
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);
@@ -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
- import raw from '../../build/appMeta.json';
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
- import raw from '../../build/auth.json';
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);
@@ -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
- import raw from '../../build/config.json';
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
- import raw from '../../build/i18n.json';
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);
@@ -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
- import raw from '../../build/logger.json';
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);
@@ -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
- import raw from '../../build/theme.json';
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.js';
20
+ import AuthConfigured from './AuthConfigured.jsx';
20
21
  import AuthNotConfigured from './AuthNotConfigured.js';
21
22
 
22
- import authConfig from '../../build/auth.js';
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) {
@@ -13,12 +13,30 @@
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, { useEffect, useRef } from 'react';
19
- import { getSession, SessionProvider, signIn, signOut, useSession } from 'next-auth/react';
20
+ import {
21
+ authConfigManager,
22
+ getSession,
23
+ SessionProvider,
24
+ signIn,
25
+ signOut,
26
+ useSession,
27
+ } from '@hono/auth-js/react';
28
+
29
+ import { serializer } from '@lowdefy/helpers';
30
+
31
+ import rawLowdefyConfig from '../../../build/config.json';
20
32
 
21
- import lowdefyConfig from '../../build/config.js';
33
+ const lowdefyConfig = serializer.deserialize(rawLowdefyConfig);
34
+
35
+ // @hono/auth-js/react configures its fetch paths through a module-level
36
+ // manager instead of SessionProvider props.
37
+ if (lowdefyConfig.basePath) {
38
+ authConfigManager.setConfig({ basePath: `${lowdefyConfig.basePath}/api/auth` });
39
+ }
22
40
 
23
41
  function Session({ children }) {
24
42
  const wasAuthenticated = useRef(false);
@@ -31,9 +49,8 @@ function Session({ children }) {
31
49
  }
32
50
  }, [status]);
33
51
 
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.
52
+ // If session is passed to SessionProvider from the server-rendered config
53
+ // we won't have a loading state here, but unauthenticated first loads do.
37
54
  if (status === 'loading') {
38
55
  return '';
39
56
  }
@@ -42,12 +59,8 @@ function Session({ children }) {
42
59
 
43
60
  function AuthConfigured({ authConfig, children, serverSession }) {
44
61
  const auth = { authConfig, getSession, signIn, signOut };
45
- let basePath = lowdefyConfig.basePath;
46
- if (basePath) {
47
- basePath = `${basePath}/api/auth`;
48
- }
49
62
  return (
50
- <SessionProvider session={serverSession} basePath={basePath}>
63
+ <SessionProvider session={serverSession}>
51
64
  <Session>
52
65
  {(session) => {
53
66
  auth.session = session;
@@ -14,18 +14,19 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- function setPageId(router, rootConfig) {
18
- if (router.pathname === `/404`) {
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') {
19
21
  return { redirect: false, pageId: '404' };
20
22
  }
21
- const segments = router.query.pageId;
22
- if (!segments || segments.length === 0) {
23
+ if (!location.pageId) {
23
24
  if (rootConfig.home.configured === false) {
24
25
  return { redirect: true, pageId: rootConfig.home.pageId };
25
26
  }
26
27
  return { redirect: false, pageId: rootConfig.home.pageId };
27
28
  }
28
- return { redirect: false, pageId: segments.join('/') };
29
+ return { redirect: false, pageId: location.pageId };
29
30
  }
30
31
 
31
32
  export default setPageId;
@@ -71,11 +71,6 @@ async function fetchPageConfig(url) {
71
71
  data._jsEntries = jsEntries;
72
72
  data._dynamicIcons = dynamicIcons;
73
73
 
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()}`;
78
- }
79
74
 
80
75
  return data;
81
76
  }
@@ -14,17 +14,19 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import { getNextAuthConfig } from '@lowdefy/api';
17
+ import { getAuthConfig as getApiAuthConfig } from '@lowdefy/api';
18
18
  import { getSecretsFromEnv } from '@lowdefy/node-utils';
19
19
 
20
20
  import adapters from '../../../build/plugins/auth/adapters.js';
21
+ import appMeta from '../../build/appMeta.js';
21
22
  import authJson from '../../build/auth.js';
22
23
  import callbacks from '../../../build/plugins/auth/callbacks.js';
23
24
  import events from '../../../build/plugins/auth/events.js';
24
25
  import providers from '../../../build/plugins/auth/providers.js';
25
26
 
26
- function getAuthOptions({ logger }) {
27
- return getNextAuthConfig({
27
+ function getAuthConfig({ logger }) {
28
+ return getApiAuthConfig({
29
+ appMeta,
28
30
  authJson,
29
31
  logger,
30
32
  plugins: { adapters, callbacks, events, providers },
@@ -32,4 +34,4 @@ function getAuthOptions({ logger }) {
32
34
  });
33
35
  }
34
36
 
35
- export default getAuthOptions;
37
+ export default getAuthConfig;
@@ -14,22 +14,23 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import { getServerSession as getNextAuthServerSession } from 'next-auth/next';
17
+ import { getAuthUser } from '@hono/auth-js';
18
18
 
19
19
  import authJson from '../../build/auth.js';
20
20
  import getMockSession from './getMockSession.js';
21
21
 
22
- async function getServerSession({ authOptions, req, res }) {
23
- // Check for mock user first (dev server only)
22
+ // Replaces getServerSession.js mock user first (dev only), then the
23
+ // session from the Hono context populated by initAuthConfig.
24
+ async function getSession(c) {
24
25
  const mockSession = await getMockSession();
25
26
  if (mockSession) {
26
27
  return mockSession;
27
28
  }
28
-
29
- if (authJson.configured === true) {
30
- return getNextAuthServerSession(req, res, authOptions);
29
+ if (authJson.configured !== true) {
30
+ return undefined;
31
31
  }
32
- return undefined;
32
+ const authUser = await getAuthUser(c);
33
+ return authUser?.session ?? undefined;
33
34
  }
34
35
 
35
- export default getServerSession;
36
+ export default getSession;
@@ -19,9 +19,9 @@ import path from 'path';
19
19
  import { serializer } from '@lowdefy/helpers';
20
20
  import { buildPageJit, createContext, makeId } from '@lowdefy/build/dev';
21
21
 
22
- import compileCss from './compileCss.js';
23
22
  import createLogger from './log/createLogger.js';
24
23
  import PageCache from './pageCache.mjs';
24
+ import readBuildApiArtifacts from './readBuildApiArtifacts.mjs';
25
25
 
26
26
  const jitLogger = createLogger({ name: 'jit-build' });
27
27
 
@@ -35,10 +35,10 @@ let cachedRegistry = null;
35
35
  let cachedBuildContext = null;
36
36
  let lastInvalidationMtime = null;
37
37
 
38
- // Frozen snapshot of icon imports from the initial build (what's actually in the Next.js bundle).
38
+ // Frozen snapshot of icon imports from the initial build (what's actually in the client bundle).
39
39
  // Module-level so it persists across context resets (skeleton rebuilds update iconImports.json
40
- // with newly discovered icons, but those aren't in the bundle until the next nextBuild).
41
- // Only resets when the server process restarts (which happens after every nextBuild).
40
+ // with newly discovered icons, but those aren't in the bundle until a server restart).
41
+ // Only resets when the server process restarts.
42
42
  let bundledIconImports = null;
43
43
 
44
44
  function readJsonFile(filePath) {
@@ -128,10 +128,15 @@ function getBuildContext(buildDirectory, configDirectory) {
128
128
  Object.assign(cachedBuildContext.modules, modules);
129
129
  }
130
130
 
131
+ // Restore api endpoint configs so JIT CallAPI validation (validateCallApiRefs in
132
+ // buildPageJit) can resolve endpointIds. Without this the dev context has no
133
+ // components.api and every CallAPI action is flagged as a non-existent endpoint.
134
+ cachedBuildContext.components = { api: readBuildApiArtifacts(buildDirectory) };
135
+
131
136
  // Use the frozen icon imports from the initial build for JIT detection.
132
- // This represents what's actually in the Next.js bundle — not what shallowBuild
137
+ // This represents what's actually in the client bundle — not what shallowBuild
133
138
  // discovers on subsequent rebuilds (those icons aren't bundled yet).
134
- // bundledIconImports is module-level and only resets on server restart (after nextBuild).
139
+ // bundledIconImports is module-level and only resets on server restart.
135
140
  if (!bundledIconImports) {
136
141
  bundledIconImports = readJsonFile(path.join(buildDirectory, 'iconImports.json')) ?? [];
137
142
  }
@@ -184,7 +189,12 @@ async function buildPageIfNeeded({ pageId, buildDirectory, configDirectory }) {
184
189
  return result;
185
190
  }
186
191
  pageCache.markCompiled(pageId);
187
- await compileCss(buildDirectory);
192
+ // Touch the candidates file so Vite's CSS pipeline re-runs Tailwind for
193
+ // classes the JIT build discovered — globals.css imports it.
194
+ fs.writeFileSync(
195
+ path.join(buildDirectory, 'tailwind-candidates.css'),
196
+ `/* Generated by Lowdefy build — rewritten on page changes to trigger CSS recompilation */\n/* ${Date.now()} */\n`
197
+ );
188
198
  jitLogger.info(
189
199
  { spin: 'succeed', color: 'white' },
190
200
  `Built page "${pageId}" in ${formatDuration(Date.now() - startTime)}.`