@lowdefy/server-dev 5.5.1 → 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.
Files changed (214) hide show
  1. package/{pages/_app.js → client/App.jsx} +21 -33
  2. package/client/DevStreamContext.js +30 -0
  3. package/client/Inspector.jsx +226 -0
  4. package/{lib/client/Page.js → client/Page.jsx} +4 -8
  5. package/client/Reload.jsx +85 -0
  6. package/client/Routing.jsx +117 -0
  7. package/client/feedback/FeedbackMount.jsx +83 -0
  8. package/client/feedback/FeedbackOverlay.jsx +901 -0
  9. package/client/feedback/captureTabScreenshot.js +84 -0
  10. package/client/feedback/copyToClipboard.js +45 -0
  11. package/client/feedback/drawAnnotationsSvg.js +119 -0
  12. package/client/feedback/elementInspect.js +99 -0
  13. package/client/feedback/feedbackStyles.js +301 -0
  14. package/client/feedback/pinResponsiveImages.js +59 -0
  15. package/client/feedback/sendFeedback.js +39 -0
  16. package/client/feedback/useFeedbackToggle.js +70 -0
  17. package/client/main.jsx +42 -0
  18. package/client/openInEditor/OpenInEditorListener.jsx +268 -0
  19. package/client/openInEditor/findBlockLocation.js +51 -0
  20. package/lib/build/app.js +8 -1
  21. package/lib/build/appMeta.js +8 -1
  22. package/lib/build/auth.js +8 -1
  23. package/lib/build/config.js +8 -1
  24. package/lib/build/i18n.js +8 -1
  25. package/lib/build/logger.js +8 -1
  26. package/lib/build/theme.js +8 -1
  27. package/lib/client/auth/{Auth.js → Auth.jsx} +11 -2
  28. package/lib/client/auth/{AuthConfigured.js → AuthConfigured.jsx} +23 -10
  29. package/lib/client/setPageId.js +6 -5
  30. package/lib/client/utils/useMutateCache.js +14 -1
  31. package/lib/client/utils/usePageConfig.js +45 -43
  32. package/lib/client/utils/usePageConfig.test.mjs +62 -0
  33. package/lib/docs/captureAnnotatedScreenshot.js +88 -0
  34. package/lib/docs/checkpointPaths.js +91 -0
  35. package/lib/docs/checkpointStore.js +240 -0
  36. package/lib/docs/checkpointToMocks.js +65 -0
  37. package/lib/docs/clientErrorStore.js +36 -0
  38. package/lib/docs/configCheckpoints.test.mjs +156 -0
  39. package/lib/docs/createConfigCheckpoint.js +92 -0
  40. package/lib/docs/createDocsMcpServer.js +531 -0
  41. package/lib/docs/createDocsMcpServer.test.mjs +141 -0
  42. package/lib/docs/devMockRegistry.js +68 -0
  43. package/lib/docs/docs.test.mjs +448 -0
  44. package/lib/docs/enrichFeedback.js +71 -0
  45. package/lib/docs/evalOperator.js +47 -0
  46. package/lib/docs/evalOperator.test.mjs +157 -0
  47. package/lib/docs/evalOperatorHeadless.js +87 -0
  48. package/lib/docs/evalOperatorHeadless.test.mjs +72 -0
  49. package/lib/docs/evalOperatorInTab.js +48 -0
  50. package/lib/docs/findConfig.js +260 -0
  51. package/lib/docs/formatFeedback.js +121 -0
  52. package/lib/docs/formatFeedback.test.mjs +204 -0
  53. package/lib/docs/getAppMap.js +180 -0
  54. package/lib/docs/getBrowser.js +127 -0
  55. package/lib/docs/getBrowser.test.mjs +122 -0
  56. package/lib/docs/getBuildStatus.js +37 -0
  57. package/lib/docs/getCoreDoc.js +63 -0
  58. package/lib/docs/getDocsManifest.js +40 -0
  59. package/lib/docs/getExamples.js +58 -0
  60. package/lib/docs/getOverview.js +115 -0
  61. package/lib/docs/getPageConfig.js +53 -0
  62. package/lib/docs/getPluginDoc.js +55 -0
  63. package/lib/docs/getSchema.js +65 -0
  64. package/lib/docs/inspectState.js +47 -0
  65. package/lib/docs/inspectState.test.mjs +140 -0
  66. package/lib/docs/inspectStateFromTab.js +40 -0
  67. package/lib/docs/inspectStateHeadless.js +84 -0
  68. package/lib/docs/inspectStateHeadless.test.mjs +60 -0
  69. package/lib/docs/isWriteRequestsAllowed.js +50 -0
  70. package/lib/docs/listConfigCheckpoints.js +55 -0
  71. package/lib/docs/listPlugins.js +80 -0
  72. package/lib/docs/listTypes.js +105 -0
  73. package/lib/docs/loadState.js +152 -0
  74. package/lib/docs/mapPageBuildErrors.js +28 -0
  75. package/lib/docs/normalizeTypeKind.js +40 -0
  76. package/{pages/api/endpoints/[...endpointId].js → lib/docs/readBuildArtifact.js} +15 -12
  77. package/lib/docs/readPageArtifact.js +26 -0
  78. package/lib/docs/resolvePluginDir.js +49 -0
  79. package/lib/docs/resolveSource.js +48 -0
  80. package/lib/docs/resolveSource.test.mjs +100 -0
  81. package/lib/docs/revertConfigCheckpoint.js +73 -0
  82. package/lib/docs/runRequest.js +141 -0
  83. package/lib/docs/saveAnnotatedScreenshot.js +43 -0
  84. package/lib/docs/scaffoldPage.js +75 -0
  85. package/lib/docs/scaffoldPage.test.mjs +97 -0
  86. package/lib/docs/screenshotPage.js +110 -0
  87. package/lib/docs/screenshotPage.test.mjs +59 -0
  88. package/lib/docs/screenshotPageClip.test.mjs +102 -0
  89. package/lib/docs/searchDocs.js +67 -0
  90. package/lib/docs/setupTestFixtures.mjs +335 -0
  91. package/lib/docs/snapshotState.js +59 -0
  92. package/lib/docs/stateCheckpoints.test.mjs +366 -0
  93. package/lib/docs/tabAvailable.js +28 -0
  94. package/lib/docs/tabChannel.js +119 -0
  95. package/lib/docs/tabChannel.test.mjs +130 -0
  96. package/lib/server/auth/{getAuthOptions.js → getAuthConfig.js} +7 -4
  97. package/lib/server/auth/getDevSession.js +70 -0
  98. package/lib/server/auth/getDevSession.test.mjs +138 -0
  99. package/lib/server/auth/getHeadlessUser.js +40 -0
  100. package/lib/server/auth/{getMockSession.js → getMockUser.js} +8 -21
  101. package/lib/server/auth/headlessUser.js +29 -0
  102. package/lib/server/auth/resolveHeadlessUser.js +38 -0
  103. package/lib/server/auth/resolveHeadlessUser.test.mjs +78 -0
  104. package/lib/server/auth/session.js +39 -0
  105. package/lib/server/auth/strategies.js +47 -0
  106. package/lib/server/{apiWrapper.js → createLowdefyContext.js} +63 -57
  107. package/lib/server/getPageJitEnrichment.test.mjs +105 -0
  108. package/lib/server/jitPageBuilder.js +125 -12
  109. package/lib/server/log/createHandleError.js +9 -0
  110. package/lib/server/log/createHandleError.test.mjs +126 -0
  111. package/manager/getContext.mjs +3 -7
  112. package/manager/processes/initialBuild.mjs +0 -2
  113. package/manager/processes/installPlugins.mjs +19 -11
  114. package/manager/processes/lowdefyBuild.mjs +29 -7
  115. package/manager/processes/lowdefyBuild.test.mjs +102 -0
  116. package/manager/processes/readDotEnv.mjs +13 -1
  117. package/manager/processes/shutdownServer.mjs +6 -6
  118. package/manager/processes/startProxy.mjs +160 -0
  119. package/manager/processes/startProxy.test.mjs +102 -0
  120. package/manager/processes/startServer.mjs +33 -16
  121. package/manager/processes/startWatchers.mjs +2 -2
  122. package/manager/processes/warnAuthUrlPortMismatch.mjs +47 -0
  123. package/manager/processes/warnAuthUrlPortMismatch.test.mjs +93 -0
  124. package/manager/run.mjs +50 -0
  125. package/manager/utils/createCustomPluginMessagesMap.mjs +8 -1
  126. package/manager/utils/createCustomPluginTypesMap.mjs +1 -0
  127. package/manager/utils/formatNoticeBox.mjs +30 -0
  128. package/manager/utils/{getNextBin.mjs → getViteBin.mjs} +7 -10
  129. package/manager/utils/updatePageTailwindCss.mjs +7 -1
  130. package/manager/utils/writeBuildStatus.mjs +39 -0
  131. package/manager/utils/writeBuildStatus.test.mjs +76 -0
  132. package/manager/watchers/lowdefyBuildWatcher.mjs +0 -1
  133. package/manager/watchers/moduleBuildWatcher.mjs +0 -1
  134. package/manager/watchers/{nextBuildWatcher.mjs → serverArtifactWatcher.mjs} +18 -20
  135. package/package.json +61 -47
  136. package/package.original.json +64 -55
  137. package/postcss.config.cjs +1 -0
  138. package/src/app.js +175 -0
  139. package/src/html/renderDevPage.js +86 -0
  140. package/src/lib/getPathSegments.js +25 -0
  141. package/src/lib/safeScriptJson.js +38 -0
  142. package/src/middleware/apiContext.js +32 -0
  143. package/src/middleware/errorHandler.js +48 -0
  144. package/src/middleware/errorHandler.test.mjs +235 -0
  145. package/src/routes/agent.js +64 -0
  146. package/src/routes/apiPage.js +37 -0
  147. package/src/routes/auth.js +49 -0
  148. package/src/routes/auth.test.mjs +84 -0
  149. package/src/routes/clientError.js +57 -0
  150. package/src/routes/cron.js +47 -0
  151. package/src/routes/cronForward.js +47 -0
  152. package/src/routes/detached.js +47 -0
  153. package/src/routes/devInspect.js +111 -0
  154. package/src/routes/devInspect.test.mjs +119 -0
  155. package/{pages/[[...pageId]].js → src/routes/devTools.js} +4 -2
  156. package/{pages/404.js → src/routes/docs/appMap.js} +6 -2
  157. package/{pages/api/dev-tools.js → src/routes/docs/buildStatus.js} +5 -4
  158. package/src/routes/docs/checkpointsCreate.js +28 -0
  159. package/src/routes/docs/checkpointsList.js +23 -0
  160. package/{pages/api/icons/dynamic.js → src/routes/docs/checkpointsRevert.js} +11 -12
  161. package/src/routes/docs/content.js +31 -0
  162. package/src/routes/docs/evalOperator.js +56 -0
  163. package/src/routes/docs/examples.js +33 -0
  164. package/src/routes/docs/find.js +30 -0
  165. package/src/routes/docs/index.js +23 -0
  166. package/src/routes/docs/inspectState.js +44 -0
  167. package/src/routes/docs/loadState.js +49 -0
  168. package/src/routes/docs/mcp.js +30 -0
  169. package/src/routes/docs/pageConfig.js +36 -0
  170. package/src/routes/docs/parseUserParam.js +52 -0
  171. package/src/routes/docs/parseUserParam.test.mjs +45 -0
  172. package/src/routes/docs/pluginDoc.js +33 -0
  173. package/src/routes/docs/plugins.js +23 -0
  174. package/src/routes/docs/runRequest.js +36 -0
  175. package/{lib/server/auth/getServerSession.js → src/routes/docs/schema.js} +17 -15
  176. package/src/routes/docs/screenshot.js +72 -0
  177. package/src/routes/docs/search.js +27 -0
  178. package/src/routes/docs/snapshotState.js +36 -0
  179. package/src/routes/docs/stateCheckpointsList.js +26 -0
  180. package/src/routes/docs/types.js +33 -0
  181. package/src/routes/endpoints.js +71 -0
  182. package/src/routes/feedback.js +110 -0
  183. package/src/routes/feedback.test.mjs +204 -0
  184. package/src/routes/jitPage.js +86 -0
  185. package/src/routes/mcp.js +34 -0
  186. package/{pages/api → src/routes}/ping.js +4 -2
  187. package/src/routes/reload.js +79 -0
  188. package/src/routes/request.js +74 -0
  189. package/src/routes/request.test.mjs +124 -0
  190. package/{pages/api → src/routes}/root.js +4 -4
  191. package/src/routes/usage.js +46 -0
  192. package/src/routes/websocket.js +33 -0
  193. package/src/websocket/devWebSocket.js +78 -0
  194. package/vite.config.js +111 -0
  195. package/.eslintrc.yaml +0 -1
  196. package/lib/client/App.js +0 -80
  197. package/lib/client/Reload.js +0 -60
  198. package/lib/server/compileCss.js +0 -39
  199. package/manager/processes/compileCss.mjs +0 -54
  200. package/manager/processes/nextBuild.mjs +0 -50
  201. package/next.config.js +0 -25
  202. package/pages/_document.js +0 -107
  203. package/pages/api/agent/[...path].js +0 -86
  204. package/pages/api/auth/[...nextauth].js +0 -48
  205. package/pages/api/client-error.js +0 -45
  206. package/pages/api/js/[env].js +0 -42
  207. package/pages/api/page/[...pageId].js +0 -74
  208. package/pages/api/reload.js +0 -53
  209. package/pages/api/request/[...path].js +0 -41
  210. /package/lib/client/{BuildErrorPage.js → BuildErrorPage.jsx} +0 -0
  211. /package/lib/client/{BuildingPage.js → BuildingPage.jsx} +0 -0
  212. /package/lib/client/{ErrorBar.js → ErrorBar.jsx} +0 -0
  213. /package/lib/client/{InstallingPluginsPage.js → InstallingPluginsPage.jsx} +0 -0
  214. /package/lib/client/{RestartingPage.js → RestartingPage.jsx} +0 -0
@@ -0,0 +1,33 @@
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 { setPendingWebSocketContext } from '../websocket/devWebSocket.js';
18
+
19
+ // In dev, Vite owns the HTTP server, so websocket upgrades can't flow through
20
+ // @hono/node-server. The Vite plugin (see vite.config.js) fetches this route
21
+ // with the upgrade request's headers to run the full middleware chain (auth
22
+ // session, apiContext), and this handler hands the built context back to the
23
+ // upgrade handler through a pending-context exchange keyed by request id.
24
+ function websocketHandler(c) {
25
+ const rid = c.req.header('x-lowdefy-websocket-rid');
26
+ if (!rid) {
27
+ return c.json({ message: 'WebSocket upgrade required.' }, 400);
28
+ }
29
+ setPendingWebSocketContext(rid, c.get('lowdefyContext'));
30
+ return c.json({ ok: true });
31
+ }
32
+
33
+ export default websocketHandler;
@@ -0,0 +1,78 @@
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 { randomUUID } from 'node:crypto';
18
+ import { createChannelRegistry, createWebSocketConnection } from '@lowdefy/api';
19
+
20
+ import app from '../app.js';
21
+
22
+ // One registry per dev server process, shared by all websocket connections.
23
+ const registry = createChannelRegistry();
24
+
25
+ // Contexts built by the /api/websocket route (full middleware chain) are
26
+ // handed to the upgrade handler through this exchange, keyed by request id.
27
+ const pendingContexts = new Map();
28
+
29
+ function setPendingWebSocketContext(rid, context) {
30
+ pendingContexts.set(rid, context);
31
+ }
32
+
33
+ function takePendingWebSocketContext(rid) {
34
+ const context = pendingContexts.get(rid);
35
+ pendingContexts.delete(rid);
36
+ return context;
37
+ }
38
+
39
+ // Called by the Vite plugin on an HTTP upgrade for /api/websocket. Runs the
40
+ // dev Hono app for the upgrade request (auth session + apiContext), then
41
+ // completes the websocket handshake and wires the connection to the registry.
42
+ async function handleWebSocketUpgrade({ request, socket, head, wss }) {
43
+ const rid = randomUUID();
44
+ const headers = new Headers();
45
+ Object.entries(request.headers).forEach(([key, value]) => {
46
+ if (value) {
47
+ headers.set(key, Array.isArray(value) ? value[0] : value);
48
+ }
49
+ });
50
+ headers.set('x-lowdefy-websocket-rid', rid);
51
+
52
+ const url = `http://${request.headers.host ?? 'localhost'}${request.url}`;
53
+ const response = await app.fetch(new Request(url, { headers }));
54
+ const context = takePendingWebSocketContext(rid);
55
+
56
+ if (!context || response.status !== 200) {
57
+ socket.end('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
58
+ return;
59
+ }
60
+
61
+ wss.handleUpgrade(request, socket, head, (ws) => {
62
+ const connection = createWebSocketConnection(context, {
63
+ registry,
64
+ send: (message) => ws.send(message),
65
+ });
66
+ ws.on('message', (data) => {
67
+ connection.handleMessage(data.toString());
68
+ });
69
+ ws.on('close', () => {
70
+ connection.close();
71
+ });
72
+ ws.on('error', (error) => {
73
+ context.logger.debug({ event: 'ws_error' }, String(error?.message ?? error));
74
+ });
75
+ });
76
+ }
77
+
78
+ export { handleWebSocketUpgrade, setPendingWebSocketContext };
package/vite.config.js ADDED
@@ -0,0 +1,111 @@
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 fs from 'node:fs';
18
+ import { defineConfig } from 'vite';
19
+ import react from '@vitejs/plugin-react';
20
+ import devServer from '@hono/vite-dev-server';
21
+ import { WebSocketServer } from 'ws';
22
+
23
+ // basePath from the Lowdefy build — assets and routes are served under it.
24
+ let basePath = '';
25
+ try {
26
+ const config = JSON.parse(fs.readFileSync('./build/config.json', 'utf8'));
27
+ basePath = config.basePath ?? '';
28
+ } catch (e) {
29
+ // No build yet — default base.
30
+ }
31
+
32
+ // Vite owns the HTTP server in dev, so /api/websocket upgrades are handled
33
+ // here instead of @hono/node-server. The upgrade handler module is loaded
34
+ // through Vite's SSR graph so it shares the Hono app and @lowdefy/api module
35
+ // instances with the dev server routes. Vite's own HMR websocket uses a
36
+ // different path and is untouched.
37
+ function lowdefyWebSocket() {
38
+ return {
39
+ name: 'lowdefy-websocket',
40
+ configureServer(server) {
41
+ const wss = new WebSocketServer({ noServer: true, maxPayload: 256 * 1024 });
42
+ server.httpServer?.on('upgrade', async (request, socket, head) => {
43
+ const { pathname } = new URL(request.url ?? '/', 'http://localhost');
44
+ if (pathname !== `${basePath}/api/websocket`) {
45
+ return;
46
+ }
47
+ try {
48
+ const { handleWebSocketUpgrade } = await server.ssrLoadModule(
49
+ './src/websocket/devWebSocket.js'
50
+ );
51
+ await handleWebSocketUpgrade({ request, socket, head, wss });
52
+ } catch (error) {
53
+ console.error(error);
54
+ socket.destroy();
55
+ }
56
+ });
57
+ },
58
+ };
59
+ }
60
+
61
+ export default defineConfig(({ mode }) => ({
62
+ base: `${basePath}/`,
63
+ plugins: [
64
+ react(),
65
+ lowdefyWebSocket(),
66
+ devServer({
67
+ entry: './src/app.js',
68
+ // Vite serves these itself; everything else routes to the Hono app.
69
+ exclude: [
70
+ /^\/client\/.+/,
71
+ /^\/lib\/.+/,
72
+ /^\/build\/.+/,
73
+ /^\/@.+$/,
74
+ /^\/node_modules\/.*/,
75
+ /\?t=\d+$/,
76
+ /^\/favicon\.ico$/,
77
+ ],
78
+ }),
79
+ ],
80
+ define: {
81
+ // Vite does not replace process.env.NODE_ENV inside dependencies —
82
+ // plugin and client code branch on it.
83
+ 'process.env.NODE_ENV': JSON.stringify(mode === 'production' ? 'production' : 'development'),
84
+ },
85
+ optimizeDeps: {
86
+ // There is no index.html in the root (the Hono app renders HTML), so the
87
+ // dependency scanner has nothing to crawl and would discover client deps
88
+ // lazily at first request — ending in a mid-session "optimized
89
+ // dependencies changed" full reload. The client entry reaches every
90
+ // plugin import statically (main.jsx → App → Routing → build/plugins/*),
91
+ // so a startup crawl discovers the full set. Linked workspace plugins are
92
+ // intentionally not pre-bundled (Vite default) so local plugin edits stay
93
+ // live.
94
+ entries: ['client/main.jsx'],
95
+ },
96
+ resolve: {
97
+ // linked plugin packages (pnpm link: / workspace) must share one React.
98
+ dedupe: ['react', 'react-dom'],
99
+ },
100
+ server: {
101
+ watch: {
102
+ // Tailwind scan inputs are rewritten by JIT page builds, and Vite
103
+ // full-reloads the browser on any watched .html change — killing
104
+ // in-flight requests on every first page visit. CSS recompilation does
105
+ // not need these events: globals.css imports build/tailwind-candidates.css,
106
+ // which the JIT builder touches whenever tailwind content changes (the
107
+ // sole recompile trigger — see writeGlobalsCss in @lowdefy/build).
108
+ ignored: ['**/lowdefy-build/tailwind/**'],
109
+ },
110
+ },
111
+ }));
package/.eslintrc.yaml DELETED
@@ -1 +0,0 @@
1
- extends: 'plugin:@next/next/core-web-vitals'
package/lib/client/App.js DELETED
@@ -1,80 +0,0 @@
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;
@@ -1,60 +0,0 @@
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, useState } from 'react';
18
-
19
- import useMutateCache from './utils/useMutateCache.js';
20
- import waitForRestartedServer from './utils/waitForRestartedServer.js';
21
-
22
- const Reload = ({ children, basePath, lowdefy }) => {
23
- const [reset, setReset] = useState(false);
24
- const [restarting, setRestarting] = useState(false);
25
- const mutateCache = useMutateCache(basePath);
26
- useEffect(() => {
27
- const sse = new EventSource(`${basePath}/api/reload`);
28
-
29
- sse.addEventListener('reload', () => {
30
- // add a update delay to prevent rerender before server is shut down for rebuild, ideally we don't want to do this.
31
- // TODO: We need to pass a flag when a rebuild will happen so that client does not trigger render.
32
- setTimeout(async () => {
33
- await mutateCache();
34
- if (lowdefy._internal?.initialised) {
35
- lowdefy._internal.initialised = false;
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
- }
42
- setReset(true);
43
- console.log('Reloaded config.');
44
- }, 600);
45
- });
46
-
47
- sse.onerror = () => {
48
- setRestarting(true);
49
- console.log('Rebuilding Lowdefy App.');
50
- sse.close();
51
- waitForRestartedServer(basePath);
52
- };
53
- return () => {
54
- sse.close();
55
- };
56
- }, []);
57
- return <>{children({ reset, setReset, restarting })}</>;
58
- };
59
-
60
- export default Reload;
@@ -1,39 +0,0 @@
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 fs from 'fs';
18
- import path from 'path';
19
- import postcss from 'postcss';
20
- import tailwindcssPlugin from '@tailwindcss/postcss';
21
-
22
- async function compileCss(buildDirectory) {
23
- const serverDirectory = path.resolve(buildDirectory, '..');
24
- const inputPath = path.join(buildDirectory, 'globals.css');
25
- const outputPath = path.join(serverDirectory, 'public/tailwind-jit.css');
26
- if (!fs.existsSync(inputPath)) return;
27
- const css = fs.readFileSync(inputPath, 'utf8');
28
- const result = await postcss([tailwindcssPlugin()]).process(css, {
29
- from: inputPath,
30
- to: outputPath,
31
- });
32
- // The Tailwind PostCSS plugin consumes the @layer order declaration from
33
- // globals.css during compilation. Re-inject it as the first line so the
34
- // browser establishes the correct cascade priority (antd > base/preflight).
35
- const layerOrder = '@layer theme, base, antd, components, utilities;\n';
36
- fs.writeFileSync(outputPath, layerOrder + result.css);
37
- }
38
-
39
- export default compileCss;
@@ -1,54 +0,0 @@
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 fs from 'fs';
18
- import path from 'path';
19
- import postcss from 'postcss';
20
- import tailwindcssPlugin from '@tailwindcss/postcss';
21
-
22
- function formatDuration(ms) {
23
- if (ms < 1000) return `${ms}ms`;
24
- return `${(ms / 1000).toFixed(2)}s`;
25
- }
26
-
27
- function compileCss({ directories, logger }) {
28
- return async () => {
29
- const inputPath = path.join(directories.build, 'globals.css');
30
- const outputPath = path.join(directories.server, 'public/tailwind-jit.css');
31
- if (!fs.existsSync(inputPath)) return;
32
-
33
- logger.info({ spin: 'start' }, 'Compiling CSS...');
34
- const startTime = Date.now();
35
-
36
- const css = fs.readFileSync(inputPath, 'utf8');
37
- const result = await postcss([tailwindcssPlugin()]).process(css, {
38
- from: inputPath,
39
- to: outputPath,
40
- });
41
- // The Tailwind PostCSS plugin consumes the @layer order declaration from
42
- // globals.css during compilation. Re-inject it as the first line so the
43
- // browser establishes the correct cascade priority (antd > base/preflight).
44
- const layerOrder = '@layer theme, base, antd, components, utilities;\n';
45
- fs.writeFileSync(outputPath, layerOrder + result.css);
46
-
47
- logger.info(
48
- { spin: 'succeed' },
49
- `Compiled CSS in ${formatDuration(Date.now() - startTime)}.`
50
- );
51
- };
52
- }
53
-
54
- export default compileCss;
@@ -1,50 +0,0 @@
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 { BuildError } from '@lowdefy/errors';
18
- import { spawnProcess } from '@lowdefy/node-utils';
19
-
20
- function formatDuration(ms) {
21
- if (ms < 1000) return `${ms}ms`;
22
- return `${(ms / 1000).toFixed(2)}s`;
23
- }
24
-
25
- function nextBuild({ bin, logger }) {
26
- return async () => {
27
- logger.info({ spin: 'start' }, 'Building app...');
28
- const startTime = Date.now();
29
- const errorLines = [];
30
- try {
31
- await spawnProcess({
32
- command: 'node',
33
- args: ['--max-old-space-size=4096', bin.next, 'build'],
34
- stdOutLineHandler: (line) => logger.debug(line),
35
- stdErrLineHandler: (line) => {
36
- logger.debug(line);
37
- errorLines.push(line);
38
- },
39
- });
40
- } catch (err) {
41
- if (errorLines.length > 0) {
42
- errorLines.forEach((line) => logger.error(line));
43
- }
44
- throw new BuildError('Next.js build failed. See above for details.');
45
- }
46
- logger.info({ spin: 'succeed' }, `Built app in ${formatDuration(Date.now() - startTime)}.`);
47
- };
48
- }
49
-
50
- export default nextBuild;
package/next.config.js DELETED
@@ -1,25 +0,0 @@
1
- const lowdefyConfig = require('./build/config.json');
2
- const blockPackages = require('./build/blockPackages.json');
3
- const serverExternalPackages = require('./build/serverExternalPackages.json');
4
-
5
- // Transpile @lowdefy/client plus all block plugin packages that may
6
- // contain CSS imports (e.g., AG Grid themes, loaders, markdown).
7
- // Built dynamically so custom user plugins are included automatically.
8
- const transpilePackages = [
9
- '@lowdefy/client',
10
- '@ant-design/x',
11
- '@ant-design/x-markdown',
12
- ...blockPackages,
13
- ];
14
-
15
- const nextConfig = {
16
- basePath: lowdefyConfig.basePath,
17
- // reactStrictMode: true,
18
- turbopack: {},
19
- transpilePackages,
20
- serverExternalPackages,
21
- compress: false,
22
- poweredByHeader: false,
23
- };
24
-
25
- module.exports = nextConfig;
@@ -1,107 +0,0 @@
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 from 'react';
18
- import Document, { Html, Head, Main, NextScript } from 'next/document';
19
-
20
- import appJson from '../lib/build/app.js';
21
- import lowdefyConfig from '../lib/build/config.js';
22
- import themeConfig from '../lib/build/theme.js';
23
-
24
- const basePath = lowdefyConfig.basePath ?? '';
25
- const VALID_COLOR_MODES = ['system', 'light', 'dark'];
26
- const configColorMode = VALID_COLOR_MODES.includes(themeConfig.darkMode)
27
- ? themeConfig.darkMode
28
- : 'system';
29
- const darkBg = themeConfig?.antd?.darkToken?.colorBgLayout ?? '#000';
30
- const lightBg = themeConfig?.antd?.lightToken?.colorBgLayout ?? '';
31
-
32
- // Escape characters that could break out of the enclosing <script> tag or
33
- // terminate a JS string literal. Used to defuse the js/bad-code-sanitization
34
- // class of injection for values embedded into the pre-hydration inline script.
35
- const SCRIPT_ESCAPES = {
36
- '<': '\\u003C',
37
- '>': '\\u003E',
38
- '\b': '\\b',
39
- '\f': '\\f',
40
- '\n': '\\n',
41
- '\r': '\\r',
42
- '\t': '\\t',
43
- '\0': '\\0',
44
- '\u2028': '\\u2028',
45
- '\u2029': '\\u2029',
46
- };
47
- function safeScriptJson(value) {
48
- return JSON.stringify(value).replace(/[<>\b\f\n\r\t\0\u2028\u2029]/g, (c) => SCRIPT_ESCAPES[c]);
49
- }
50
-
51
- class LowdefyDocument extends Document {
52
- render() {
53
- return (
54
- <Html className="lowdefy">
55
- <Head>
56
- {/* Synchronous script that creates the @layer order declaration and keeps
57
- it as the first child of <head> via MutationObserver. antd's CSS-in-JS
58
- uses prependQueue to inject <style> tags at the top of <head>, which
59
- would otherwise make @layer antd the first (lowest priority) layer.
60
- MutationObserver fires before paint, so the browser never sees the
61
- wrong cascade order. */}
62
- <script
63
- dangerouslySetInnerHTML={{
64
- __html: `(function(){var s=document.createElement("style");s.id="__lf-layer-order";s.textContent="@layer theme, base, antd, components, utilities;";document.head.prepend(s);new MutationObserver(function(){if(document.head.firstChild!==s)document.head.prepend(s)}).observe(document.head,{childList:true})})();`,
65
- }}
66
- />
67
- {/* Synchronous pre-hydration background script — prevents mode-mismatch
68
- flash on page navigation. Mirrors useDarkMode.js resolution order:
69
- configDarkMode → localStorage → prefers-color-scheme. Uses the user's
70
- configured colorBgLayout tokens when present (theme.antd.darkToken and
71
- theme.antd.lightToken), falling back to #000 in dark and no inline style
72
- in light so default behavior is unchanged. */}
73
- <script
74
- dangerouslySetInnerHTML={{
75
- __html: `(function(){var c=${safeScriptJson(configColorMode)};var db=${safeScriptJson(
76
- darkBg
77
- )};var lb=${safeScriptJson(
78
- lightBg
79
- )};var d;if(c==="dark")d=true;else if(c==="light")d=false;else{try{var p=localStorage.getItem("lowdefy_darkMode");if(p==="dark")d=true;else if(p==="light")d=false;else d=window.matchMedia("(prefers-color-scheme:dark)").matches}catch(e){d=window.matchMedia("(prefers-color-scheme:dark)").matches}}var bg=d?db:lb;if(bg)document.documentElement.style.backgroundColor=bg})();`,
80
- }}
81
- />
82
- <link rel="manifest" href={`${basePath}/manifest.webmanifest`} />
83
- <link rel="icon" type="image/svg+xml" href={`${basePath}/icon.svg`} />
84
- <link rel="apple-touch-icon" href={`${basePath}/apple-touch-icon.png`} />
85
- <link id="tailwind-jit-css" rel="stylesheet" href={`${basePath}/tailwind-jit.css`} />
86
- <meta name="viewport" content="width=device-width, initial-scale=1" />
87
- <script
88
- dangerouslySetInnerHTML={{
89
- __html: `/* start of Lowdefy append head */</script>${appJson.html.appendHead}<script>/* end of Lowdefy append head */`,
90
- }}
91
- />
92
- </Head>
93
- <body>
94
- <Main />
95
- <NextScript />
96
- <script
97
- dangerouslySetInnerHTML={{
98
- __html: `/* start of Lowdefy append body */</script>${appJson.html.appendBody}<script>/* end of Lowdefy append body */`,
99
- }}
100
- />
101
- </body>
102
- </Html>
103
- );
104
- }
105
- }
106
-
107
- export default LowdefyDocument;