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

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
@@ -14,27 +14,22 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import { getAuthStrategies } from '@lowdefy/api';
17
+ import { getNextAuthConfig } from '@lowdefy/api';
18
18
  import { getSecretsFromEnv } from '@lowdefy/node-utils';
19
19
 
20
- import appMeta from '../../build/appMeta.js';
20
+ import adapters from '../../../build/plugins/auth/adapters.js';
21
21
  import authJson from '../../build/auth.js';
22
- import strategyTypes from '../../../build/plugins/auth/strategies.js';
22
+ import callbacks from '../../../build/plugins/auth/callbacks.js';
23
+ import events from '../../../build/plugins/auth/events.js';
24
+ import providers from '../../../build/plugins/auth/providers.js';
23
25
 
24
- // Returns the API auth strategy verifiers (memoized in @lowdefy/api), or an
25
- // empty list when auth is not configured - resolveAuthentication tries them
26
- // in config order when no session resolves.
27
- function getStrategies({ logger }) {
28
- if (authJson.configured !== true) {
29
- return [];
30
- }
31
- return getAuthStrategies({
32
- appMeta,
26
+ function getAuthOptions({ logger }) {
27
+ return getNextAuthConfig({
33
28
  authJson,
34
29
  logger,
35
- plugins: { strategies: strategyTypes },
30
+ plugins: { adapters, callbacks, events, providers },
36
31
  secrets: getSecretsFromEnv(),
37
32
  });
38
33
  }
39
34
 
40
- export default getStrategies;
35
+ export default getAuthOptions;
@@ -14,15 +14,13 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
+ import { createSessionCallback } from '@lowdefy/api';
17
18
  import { serializer } from '@lowdefy/helpers';
18
19
 
19
20
  import authJson from '../../build/auth.js';
21
+ import callbacks from '../../../build/plugins/auth/callbacks.js';
20
22
 
21
- // dev.mockUser is a pre-resolved caller that substitutes for the whole
22
- // resolveAuthentication step - no auth engine, no session lookup, no
23
- // membership wall. Its roles are authoritative. LOWDEFY_DEV_USER takes
24
- // priority over auth.dev.mockUser from lowdefy.yaml.
25
- function getMockUser() {
23
+ async function getMockSession() {
26
24
  const mockUserJson = process.env.LOWDEFY_DEV_USER;
27
25
  let mockUser;
28
26
 
@@ -37,7 +35,7 @@ function getMockUser() {
37
35
  }
38
36
 
39
37
  if (!mockUser) {
40
- return null;
38
+ return undefined;
41
39
  }
42
40
 
43
41
  // Deserialize to restore arrays from ~arr markers and remove other build markers
@@ -50,9 +48,20 @@ function getMockUser() {
50
48
  );
51
49
  }
52
50
 
53
- // The roles/attributes floor is applied at injection by
54
- // normalizeInjectedCaller (createLowdefyContext.js) - return the raw user.
55
- return mockUser;
51
+ // Create session callback to transform mock user
52
+ const sessionCallback = createSessionCallback({
53
+ authConfig: authJson,
54
+ plugins: { callbacks },
55
+ });
56
+
57
+ // Transform mock user through session callback (mock user acts as token)
58
+ const session = await sessionCallback({
59
+ session: { user: {} },
60
+ token: mockUser,
61
+ user: mockUser,
62
+ });
63
+
64
+ return session;
56
65
  }
57
66
 
58
- export default getMockUser;
67
+ export default getMockSession;
@@ -14,20 +14,22 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import revertConfigCheckpoint from '../../../lib/docs/revertConfigCheckpoint.js';
17
+ import { getServerSession as getNextAuthServerSession } from 'next-auth/next';
18
18
 
19
- async function docsCheckpointsRevertHandler(c) {
20
- const body = await c.req.json().catch(() => ({}));
21
- if (!body.id) {
22
- return c.json({ error: 'revert requires an "id" of the checkpoint to restore.' }, 400);
19
+ import authJson from '../../build/auth.js';
20
+ import getMockSession from './getMockSession.js';
21
+
22
+ async function getServerSession({ authOptions, req, res }) {
23
+ // Check for mock user first (dev server only)
24
+ const mockSession = await getMockSession();
25
+ if (mockSession) {
26
+ return mockSession;
23
27
  }
24
28
 
25
- try {
26
- const result = revertConfigCheckpoint({ id: body.id });
27
- return c.json(result);
28
- } catch (error) {
29
- return c.json({ error: error.message }, 400);
29
+ if (authJson.configured === true) {
30
+ return getNextAuthServerSession(req, res, authOptions);
30
31
  }
32
+ return undefined;
31
33
  }
32
34
 
33
- export default docsCheckpointsRevertHandler;
35
+ export default getServerSession;
@@ -0,0 +1,39 @@
1
+ /*
2
+ Copyright 2020-2026 Lowdefy, Inc
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ 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;
@@ -17,8 +17,9 @@
17
17
  import fs from 'fs';
18
18
  import path from 'path';
19
19
  import { serializer } from '@lowdefy/helpers';
20
- import { buildPageJit, createContext, hydrateDeferredRecords, makeId } from '@lowdefy/build/dev';
20
+ import { buildPageJit, createContext, makeId } from '@lowdefy/build/dev';
21
21
 
22
+ import compileCss from './compileCss.js';
22
23
  import createLogger from './log/createLogger.js';
23
24
  import PageCache from './pageCache.mjs';
24
25
  import readBuildApiArtifacts from './readBuildApiArtifacts.mjs';
@@ -35,10 +36,10 @@ let cachedRegistry = null;
35
36
  let cachedBuildContext = null;
36
37
  let lastInvalidationMtime = null;
37
38
 
38
- // Frozen snapshot of icon imports from the initial build (what's actually in the client bundle).
39
+ // Frozen snapshot of icon imports from the initial build (what's actually in the Next.js bundle).
39
40
  // 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 a server restart).
41
- // Only resets when the server process restarts.
41
+ // with newly discovered icons, but those aren't in the bundle until the next nextBuild).
42
+ // Only resets when the server process restarts (which happens after every nextBuild).
42
43
  let bundledIconImports = null;
43
44
 
44
45
  function readJsonFile(filePath) {
@@ -91,10 +92,10 @@ function getBuildContext(buildDirectory, configDirectory) {
91
92
  const keyMap = readJsonFile(path.join(buildDirectory, 'keyMap.json')) ?? {};
92
93
  const jsMap = readJsonFile(path.join(buildDirectory, 'jsMap.json')) ?? { client: {}, server: {} };
93
94
  const connectionIds = readJsonFile(path.join(buildDirectory, 'connectionIds.json')) ?? [];
94
- const websocketIds = readJsonFile(path.join(buildDirectory, 'websocketIds.json')) ?? [];
95
95
 
96
96
  const customTypesMap = readJsonFile(path.join(buildDirectory, 'customTypesMap.json')) ?? {};
97
- const customMessagesMap = readJsonFile(path.join(buildDirectory, 'customMessagesMap.json')) ?? {};
97
+ const customMessagesMap =
98
+ readJsonFile(path.join(buildDirectory, 'customMessagesMap.json')) ?? {};
98
99
 
99
100
  cachedBuildContext = createContext({
100
101
  customMessagesMap,
@@ -108,7 +109,7 @@ function getBuildContext(buildDirectory, configDirectory) {
108
109
  stage: 'dev',
109
110
  });
110
111
 
111
- // Restore refMap, keyMap, jsMap, connectionIds, and websocketIds from skeleton build
112
+ // Restore refMap, keyMap, jsMap, and connectionIds from skeleton build
112
113
  Object.assign(cachedBuildContext.refMap, refMap);
113
114
  Object.assign(cachedBuildContext.keyMap, keyMap);
114
115
  cachedBuildContext.jsMap.client = jsMap.client ?? {};
@@ -116,9 +117,6 @@ function getBuildContext(buildDirectory, configDirectory) {
116
117
  for (const id of connectionIds) {
117
118
  cachedBuildContext.connectionIds.add(id);
118
119
  }
119
- for (const id of websocketIds) {
120
- cachedBuildContext.websocketIds.add(id);
121
- }
122
120
 
123
121
  // Load installed packages snapshot from skeleton build for missing-package detection
124
122
  const installedPluginPackages =
@@ -131,27 +129,15 @@ function getBuildContext(buildDirectory, configDirectory) {
131
129
  Object.assign(cachedBuildContext.modules, modules);
132
130
  }
133
131
 
134
- // Hydrate the deferred-record registry — module component bodies referenced
135
- // by '~deferred' placeholders in modules.json live here. readJsonFile runs
136
- // the marker-restoring reviver, so record-body ~r/~l markers survive.
137
- const deferredRecords = readJsonFile(path.join(buildDirectory, 'deferredRecords.json'));
138
- if (deferredRecords) {
139
- hydrateDeferredRecords(cachedBuildContext, deferredRecords);
140
- }
141
-
142
- // Restore app metadata so JIT page builds resolve _app / _build.app against
143
- // the same metadata the skeleton build computed.
144
- cachedBuildContext.appMeta = readJsonFile(path.join(buildDirectory, 'appMeta.json')) ?? null;
145
-
146
132
  // Restore api endpoint configs so JIT CallAPI validation (validateCallApiRefs in
147
133
  // buildPageJit) can resolve endpointIds. Without this the dev context has no
148
134
  // components.api and every CallAPI action is flagged as a non-existent endpoint.
149
135
  cachedBuildContext.components = { api: readBuildApiArtifacts(buildDirectory) };
150
136
 
151
137
  // Use the frozen icon imports from the initial build for JIT detection.
152
- // This represents what's actually in the client bundle — not what shallowBuild
138
+ // This represents what's actually in the Next.js bundle — not what shallowBuild
153
139
  // discovers on subsequent rebuilds (those icons aren't bundled yet).
154
- // bundledIconImports is module-level and only resets on server restart.
140
+ // bundledIconImports is module-level and only resets on server restart (after nextBuild).
155
141
  if (!bundledIconImports) {
156
142
  bundledIconImports = readJsonFile(path.join(buildDirectory, 'iconImports.json')) ?? [];
157
143
  }
@@ -204,18 +190,7 @@ async function buildPageIfNeeded({ pageId, buildDirectory, configDirectory }) {
204
190
  return result;
205
191
  }
206
192
  pageCache.markCompiled(pageId);
207
- // Touch the candidates file so Vite's CSS pipeline re-runs Tailwind for
208
- // classes the JIT build discovered — globals.css imports it. This import
209
- // is the ONLY recompile trigger: the tailwind .html scan inputs are
210
- // excluded from Vite's watcher (their .html change events would force
211
- // full browser reloads). Only touched when the build actually changed
212
- // tailwind content, so unchanged rebuilds cause no CSS recompile.
213
- if (result?._tailwindChanged) {
214
- fs.writeFileSync(
215
- path.join(buildDirectory, 'tailwind-candidates.css'),
216
- `/* Generated by Lowdefy build — rewritten on page changes to trigger CSS recompilation */\n/* ${Date.now()} */\n`
217
- );
218
- }
193
+ await compileCss(buildDirectory);
219
194
  jitLogger.info(
220
195
  { spin: 'succeed', color: 'white' },
221
196
  `Built page "${pageId}" in ${formatDuration(Date.now() - startTime)}.`
@@ -16,11 +16,13 @@
16
16
 
17
17
  // TODO: Better name needed here maybe?
18
18
  function logRequest({ context }) {
19
- const user = context.user ?? {};
19
+ const { user = {} } = context;
20
20
  context.logger.debug({
21
21
  user: {
22
22
  id: user.id,
23
23
  roles: user.roles,
24
+ sub: user.sub,
25
+ session_id: user.session_id,
24
26
  },
25
27
  url: context.req.url,
26
28
  method: context.req.method,
@@ -22,16 +22,18 @@ import { hideBin } from 'yargs/helpers';
22
22
  import pino from 'pino';
23
23
  import { createNodeLogger } from '@lowdefy/logger/node';
24
24
  import checkMockUserWarning from './processes/checkMockUserWarning.mjs';
25
+ import compileCss from './processes/compileCss.mjs';
25
26
  import initialBuild from './processes/initialBuild.mjs';
26
27
  import installPlugins from './processes/installPlugins.mjs';
27
28
  import lowdefyBuild from './processes/lowdefyBuild.mjs';
29
+ import nextBuild from './processes/nextBuild.mjs';
28
30
  import readDotEnv from './processes/readDotEnv.mjs';
29
31
  import reloadClients from './processes/reloadClients.mjs';
30
32
  import restartServer from './processes/restartServer.mjs';
31
33
  import shutdownServer from './processes/shutdownServer.mjs';
32
34
  import startWatchers from './processes/startWatchers.mjs';
33
35
 
34
- import getViteBin from './utils/getViteBin.mjs';
36
+ import getNextBin from './utils/getNextBin.mjs';
35
37
 
36
38
  const argv = yargs(hideBin(process.argv)).array('watch').array('watchIgnore').argv;
37
39
 
@@ -40,7 +42,7 @@ async function getContext() {
40
42
 
41
43
  const context = {
42
44
  bin: {
43
- vite: getViteBin(),
45
+ next: getNextBin(),
44
46
  },
45
47
  directories: {
46
48
  build: path.resolve(process.cwd(), './build'),
@@ -54,7 +56,7 @@ async function getContext() {
54
56
  destination: pino.destination({ dest: 1, sync: true }),
55
57
  }),
56
58
  options: {
57
- port: Number(argv.port ?? env.PORT ?? 3000),
59
+ port: argv.port ?? env.PORT ?? 3000,
58
60
  refResolver: argv.refResolver ?? env.LOWDEFY_BUILD_REF_RESOLVER,
59
61
  watch:
60
62
  argv.watch ?? env.LOWDEFY_SERVER_DEV_WATCH ? JSON.parse(env.LOWDEFY_SERVER_DEV_WATCH) : [],
@@ -85,6 +87,8 @@ async function getContext() {
85
87
  }
86
88
  };
87
89
 
90
+ context.compileCss = compileCss(context);
91
+ context.nextBuild = nextBuild(context);
88
92
  context.readDotEnv = readDotEnv(context);
89
93
  context.reloadClients = reloadClients(context);
90
94
  context.restartServer = restartServer(context);
@@ -0,0 +1,54 @@
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;
@@ -21,6 +21,8 @@ function initialBuild(context) {
21
21
  await context.lowdefyBuild();
22
22
  await context.checkMockUserWarning();
23
23
  await context.installPlugins();
24
+ await context.nextBuild();
25
+ await context.compileCss();
24
26
  };
25
27
  }
26
28
 
@@ -14,29 +14,21 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import { installIfPackageJsonChanged, spawnProcess } from '@lowdefy/node-utils';
17
+ import { spawnProcess } from '@lowdefy/node-utils';
18
18
 
19
- function installPlugins({ directories, logger, packageManagerCmd }) {
19
+ function installPlugins({ logger, packageManagerCmd }) {
20
20
  return async () => {
21
- const installed = await installIfPackageJsonChanged({
22
- directory: directories.server,
23
- install: async () => {
24
- logger.info({ spin: 'start' }, 'Installing plugins...');
25
- await spawnProcess({
26
- processOptions: {
27
- // https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2#command-injection-via-args-parameter-of-child_processspawn-without-shell-option-enabled-on-windows-cve-2024-27980---high
28
- shell: process.platform === 'win32',
29
- },
30
- command: packageManagerCmd,
31
- args: ['install', '--no-frozen-lockfile'],
32
- stdOutLineHandler: (line) => logger.debug(line),
33
- });
34
- logger.info('Installed plugins.');
21
+ logger.info({ spin: 'start' }, 'Installing plugins...');
22
+ await spawnProcess({
23
+ processOptions: {
24
+ // https://nodejs.org/en/blog/vulnerability/april-2024-security-releases-2#command-injection-via-args-parameter-of-child_processspawn-without-shell-option-enabled-on-windows-cve-2024-27980---high
25
+ shell: process.platform === 'win32',
35
26
  },
27
+ command: packageManagerCmd,
28
+ args: ['install', '--no-frozen-lockfile'],
29
+ stdOutLineHandler: (line) => logger.debug(line),
36
30
  });
37
- if (!installed) {
38
- logger.debug('Plugin dependencies unchanged, skipped install.');
39
- }
31
+ logger.info('Installed plugins.');
40
32
  };
41
33
  }
42
34
 
@@ -14,10 +14,9 @@
14
14
  limitations under the License.
15
15
  */
16
16
 
17
- import { serializeBuildException, shallowBuild } from '@lowdefy/build/dev';
17
+ import { shallowBuild } from '@lowdefy/build/dev';
18
18
  import createCustomPluginMessagesMap from '../utils/createCustomPluginMessagesMap.mjs';
19
19
  import createCustomPluginTypesMap from '../utils/createCustomPluginTypesMap.mjs';
20
- import writeBuildStatus from '../utils/writeBuildStatus.mjs';
21
20
 
22
21
  function formatDuration(ms) {
23
22
  if (ms < 1000) return `${ms}ms`;
@@ -31,34 +30,13 @@ function lowdefyBuild({ directories, logger, options }) {
31
30
  const customTypesMap = await createCustomPluginTypesMap({ directories, logger });
32
31
  const customMessagesMap = await createCustomPluginMessagesMap({ directories, logger });
33
32
 
34
- let result;
35
- try {
36
- result = await shallowBuild({
37
- customMessagesMap,
38
- customTypesMap,
39
- directories,
40
- logger,
41
- refResolver: options.refResolver,
42
- stage: 'dev',
43
- });
44
- } catch (error) {
45
- // Write the artifact after the attempt fails - the build may have
46
- // cleaned the build directory before failing, so this must not run
47
- // before the attempt completes.
48
- await writeBuildStatus({
49
- directories,
50
- status: 'error',
51
- errors: error.errors ?? [{ message: error.message }],
52
- warnings: error.warnings ?? [],
53
- });
54
- throw error;
55
- }
56
-
57
- await writeBuildStatus({
33
+ const result = await shallowBuild({
34
+ customMessagesMap,
35
+ customTypesMap,
58
36
  directories,
59
- status: 'ok',
60
- errors: [],
61
- warnings: (result.context.warnings ?? []).map(serializeBuildException),
37
+ logger,
38
+ refResolver: options.refResolver,
39
+ stage: 'dev',
62
40
  });
63
41
 
64
42
  // Return result so getContext can store registries
@@ -0,0 +1,50 @@
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;
@@ -18,20 +18,8 @@ import path from 'path';
18
18
  import dotenv from 'dotenv';
19
19
 
20
20
  function readDotEnv(context) {
21
- // dotenv.config() never overwrites env vars that are already set, so on
22
- // the first load the real shell environment wins over .env (standard
23
- // dotenv behavior). On reloads triggered by the .env watcher the previous
24
- // load has already populated process.env, so without override the changed
25
- // values would never take effect and the server would restart with stale
26
- // env.
27
- let loadedOnce = false;
28
21
  return () => {
29
- dotenv.config({
30
- path: path.join(context.directories.config, '.env'),
31
- silent: true,
32
- override: loadedOnce,
33
- });
34
- loadedOnce = true;
22
+ dotenv.config({ path: path.join(context.directories.config, '.env'), silent: true });
35
23
  };
36
24
  }
37
25
 
@@ -16,18 +16,18 @@
16
16
 
17
17
  function shutdownServer(context) {
18
18
  return () => {
19
- if (context.devServer) {
19
+ if (context.nextServer) {
20
20
  context.logger.debug(
21
- `Existing dev server with pid ${context.devServer.pid}, killed: ${context.devServer.killed}`
21
+ `Existing next server with pid ${context.nextServer.pid}, killed: ${context.nextServer.killed}`
22
22
  );
23
- if (!context.devServer.killed) {
23
+ if (!context.nextServer.killed) {
24
24
  context.logger.info({ spin: 'start' }, 'Shutting down server...');
25
- context.devServer.kill();
25
+ context.nextServer.kill();
26
26
  context.logger.debug(
27
- `Killed dev server with pid ${context.devServer.pid}, killed: ${context.devServer.killed}`
27
+ `Killed next server with pid ${context.nextServer.pid}, killed: ${context.nextServer.killed}`
28
28
  );
29
29
  }
30
- context.devServer = null;
30
+ context.nextServer = null;
31
31
  }
32
32
  };
33
33
  }
@@ -16,7 +16,6 @@
16
16
 
17
17
  import { spawn } from 'child_process';
18
18
 
19
-
20
19
  function createStdErrLineHandler({ context }) {
21
20
  const port = context.options.port;
22
21
  return function stdErrLineHandler(line) {
@@ -33,7 +32,7 @@ function createStdErrLineHandler({ context }) {
33
32
  function startServer(context) {
34
33
  context.shutdownServer();
35
34
 
36
- const devServer = spawn('node', [context.bin.vite, '--port', String(context.options.port), '--strictPort'], {
35
+ const nextServer = spawn('node', [context.bin.next, 'start'], {
37
36
  stdio: ['ignore', 'inherit', 'pipe'],
38
37
  env: {
39
38
  ...process.env,
@@ -43,7 +42,7 @@ function startServer(context) {
43
42
  });
44
43
 
45
44
  const stdErrLineHandler = createStdErrLineHandler({ context });
46
- devServer.stderr.on('data', (data) => {
45
+ nextServer.stderr.on('data', (data) => {
47
46
  data
48
47
  .toString('utf8')
49
48
  .split('\n')
@@ -52,14 +51,14 @@ function startServer(context) {
52
51
  });
53
52
  });
54
53
 
55
- context.logger.debug(`Started dev server with pid ${devServer.pid}.`);
56
- devServer.on('exit', (code, signal) => {
57
- context.logger.debug(`devServer exit ${devServer.pid}, signal: ${signal}, code: ${code}`);
54
+ context.logger.debug(`Started next server with pid ${nextServer.pid}.`);
55
+ nextServer.on('exit', (code, signal) => {
56
+ context.logger.debug(`nextServer exit ${nextServer.pid}, signal: ${signal}, code: ${code}`);
58
57
  });
59
- devServer.on('error', (error) => {
58
+ nextServer.on('error', (error) => {
60
59
  context.logger.error(error);
61
60
  });
62
- context.devServer = devServer;
61
+ context.nextServer = nextServer;
63
62
  }
64
63
 
65
64
  export default startServer;
@@ -17,7 +17,7 @@
17
17
  import envWatcher from '../watchers/envWatcher.mjs';
18
18
  import lowdefyBuildWatcher from '../watchers/lowdefyBuildWatcher.mjs';
19
19
  import moduleBuildWatcher from '../watchers/moduleBuildWatcher.mjs';
20
- import serverArtifactWatcher from '../watchers/serverArtifactWatcher.mjs';
20
+ import nextBuildWatcher from '../watchers/nextBuildWatcher.mjs';
21
21
 
22
22
  function startWatchers(context) {
23
23
  return async () => {
@@ -25,7 +25,7 @@ function startWatchers(context) {
25
25
  envWatcher(context),
26
26
  lowdefyBuildWatcher(context),
27
27
  moduleBuildWatcher(context),
28
- serverArtifactWatcher(context),
28
+ nextBuildWatcher(context),
29
29
  ]);
30
30
  };
31
31
  }