@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
@@ -1,99 +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 { getPageConfig } from '@lowdefy/api';
18
-
19
- import authJson from '../../lib/build/auth.js';
20
- import buildPageIfNeeded from '../../lib/server/jitPageBuilder.js';
21
- import getPathSegments from '../lib/getPathSegments.js';
22
- import lowdefyConfig from '../../lib/build/config.js';
23
-
24
- const basePath = lowdefyConfig.basePath ?? '';
25
-
26
- // JIT page build + config response. The response shapes are a frozen contract
27
- // with the dev client:
28
- // 200 { installing: true, packages } — plugin install in progress, client polls
29
- // 500 { buildError: true, errors, message, source } — build failed
30
- // 401 { redirect } — logged-out navigation to a protected page
31
- // 404 'Page not found.'
32
- // 200 pageConfig (+ _warnings)
33
- async function jitPageHandler(c) {
34
- const context = c.get('lowdefyContext');
35
- const pageId = getPathSegments(c, '/api/page/').join('/');
36
-
37
- let buildResult;
38
- try {
39
- buildResult = await buildPageIfNeeded({
40
- pageId,
41
- buildDirectory: context.buildDirectory,
42
- configDirectory: context.configDirectory,
43
- });
44
- } catch (error) {
45
- const rawErrors = error.buildErrors ?? [error];
46
- const errors = [];
47
- for (const err of rawErrors) {
48
- await context.handleError(err);
49
- errors.push({
50
- type: err.name ?? 'Error',
51
- message: err.message,
52
- source: err.source ?? null,
53
- stack: err.stack ?? null,
54
- });
55
- }
56
- return c.json(
57
- {
58
- buildError: true,
59
- errors,
60
- // Keep top-level message/source for backward compatibility
61
- message: error.message,
62
- source: error.source ?? null,
63
- },
64
- 500
65
- );
66
- }
67
-
68
- if (buildResult && buildResult.installing) {
69
- return c.json({
70
- installing: true,
71
- packages: buildResult.packages,
72
- });
73
- }
74
-
75
- const result = await getPageConfig(context, { pageId, urlQuery: c.req.query() });
76
- if (result.status === 'unauthenticated') {
77
- // The client follows this redirect with a full page load, so the login
78
- // page can return to the requested page after sign-in.
79
- const callbackUrl = `${basePath}/${pageId}`;
80
- return c.json(
81
- {
82
- redirect: `${basePath}${authJson.authPages.signIn}?callbackUrl=${encodeURIComponent(
83
- callbackUrl
84
- )}`,
85
- },
86
- 401
87
- );
88
- }
89
- if (result.status !== 'ok') {
90
- return c.text('Page not found.', 404);
91
- }
92
- const pageConfig = result.pageConfig;
93
- if (buildResult?.warnings?.length > 0) {
94
- pageConfig._warnings = buildResult.warnings;
95
- }
96
- return c.json(pageConfig);
97
- }
98
-
99
- export default jitPageHandler;
@@ -1,81 +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 { randomUUID } from 'node:crypto';
18
-
19
- import chokidar from 'chokidar';
20
- import { streamSSE } from 'hono/streaming';
21
-
22
- import { registerTab, unregisterTab } from '../../lib/docs/tabChannel.js';
23
-
24
- // SSE endpoint — notifies the client when build/reload is written so it can
25
- // mutate the SWR cache and refetch config. Also doubles as the transport for
26
- // the agent-state-xray tab channel (lib/docs/tabChannel.js): a connection
27
- // that includes ?pageId=<id> is registered as an inspectable dev tab so an
28
- // agent can push it inspect-request/eval-request SSE events.
29
- //
30
- // pageId tracking design: Inspector.jsx (not Reload.jsx) owns this query
31
- // param, and re-opens its EventSource — new connection, new tab id — every
32
- // time the developer navigates to a different page. That keeps tabChannel's
33
- // view of "what page is this tab on" always correct without a separate
34
- // ping route: Reload.jsx's own connection never sends pageId, so it is never
35
- // registered as a tab and can't be mistaken for one by requestFromTab.
36
- async function reloadHandler(c) {
37
- return streamSSE(c, async (stream) => {
38
- const watcher = chokidar.watch(['./build/reload'], {
39
- persistent: true,
40
- ignoreInitial: true,
41
- });
42
-
43
- const pageId = c.req.query('pageId');
44
- const tabId = pageId === undefined ? undefined : randomUUID();
45
- if (tabId) {
46
- registerTab({
47
- id: tabId,
48
- pageId,
49
- send: (event, data) => stream.writeSSE({ event, data: JSON.stringify(data) }),
50
- });
51
- }
52
-
53
- let open = true;
54
- stream.onAbort(() => {
55
- open = false;
56
- watcher.close();
57
- if (tabId) {
58
- unregisterTab({ id: tabId });
59
- }
60
- });
61
-
62
- const reload = () => {
63
- stream.writeSSE({ event: 'reload', data: JSON.stringify({}) });
64
- };
65
- watcher.on('add', () => reload());
66
- watcher.on('change', () => reload());
67
- // Do not reload on unlink — cleanBuildDirectory deletes build/reload during
68
- // skeleton rebuilds, which would send a premature SSE event before the new
69
- // build artifacts are written. The real reload comes via add/change when
70
- // reloadClients() creates the file after the build completes.
71
-
72
- while (open) {
73
- await stream.sleep(15000);
74
- if (open) {
75
- await stream.writeSSE({ event: 'ping', data: '' });
76
- }
77
- }
78
- });
79
- }
80
-
81
- export default reloadHandler;
@@ -1,66 +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 { callRequest } from '@lowdefy/api';
18
- import { serializer } from '@lowdefy/helpers';
19
-
20
- import getPathSegments from '../lib/getPathSegments.js';
21
- import { getMock } from '../../lib/docs/devMockRegistry.js';
22
-
23
- async function requestHandler(c) {
24
- if (c.req.method !== 'POST') {
25
- throw new Error('Only POST requests are supported.');
26
- }
27
- const context = c.get('lowdefyContext');
28
- const segments = getPathSegments(c, '/api/request/');
29
- if (segments.length < 2) {
30
- return c.json({ error: 'Invalid request path' }, 400);
31
- }
32
- const requestId = segments[segments.length - 1];
33
- const pageId = segments.slice(0, -1).join('/');
34
- const { actionId, blockId, payload } = await c.req.json();
35
-
36
- // Dev-only agent tooling: once a state checkpoint has been loaded
37
- // (lib/docs/loadState.js), its recorded request/response pairs live in
38
- // devMockRegistry keyed by (pageId, requestId). Short-circuit here, before
39
- // ever touching the real connection, so a headless verification page, a
40
- // human tester's browser tab, or an e2e test replays exactly what was
41
- // recorded. The response shape mirrors @lowdefy/api's callRequest contract
42
- // (see callRequestResolver.js / callRequest.js success shape, and
43
- // middleware/errorHandler.js for the error shape) so the client's
44
- // Requests.js can't tell the difference from a real request.
45
- const mock = getMock({ pageId, requestId });
46
- if (mock) {
47
- context.logger.info({ event: 'dev_mock_request', pageId, requestId, blockId, actionId });
48
- if (mock.error) {
49
- const error = mock.error instanceof Error ? mock.error : new Error(mock.error);
50
- const serialized = serializer.serialize(error);
51
- if (serialized?.['~e']) {
52
- delete serialized['~e'].received;
53
- delete serialized['~e'].stack;
54
- delete serialized['~e'].configKey;
55
- }
56
- return c.json(serialized, 500);
57
- }
58
- return c.json({ success: true, response: serializer.serialize(mock.response) });
59
- }
60
-
61
- context.logger.info({ event: 'call_request', pageId, requestId, blockId, actionId });
62
- const response = await callRequest(context, { blockId, pageId, payload, requestId });
63
- return c.json(response);
64
- }
65
-
66
- export default requestHandler;
@@ -1,46 +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 'node:fs';
18
- import path from 'node:path';
19
-
20
- const packageJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8'));
21
-
22
- async function usageHandler(c) {
23
- if (c.req.method !== 'POST') {
24
- throw new Error('Only POST requests are supported.');
25
- }
26
- const context = c.get('lowdefyContext');
27
- const { user, machine } = await c.req.json();
28
- const host = c.req.header('host');
29
- context.logger.info({ event: 'log_usage', user, machine });
30
-
31
- const timestamp = Date.now();
32
-
33
- return c.json({
34
- offline: false,
35
- data: {
36
- gitSha: context.appMeta.gitSha,
37
- host,
38
- machine,
39
- timestamp,
40
- user,
41
- version: packageJson.version,
42
- },
43
- });
44
- }
45
-
46
- export default usageHandler;
@@ -1,27 +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
- // The resolved caller for the client - the same resolveAuthentication output
18
- // the first page load embeds. The base session carries neither roles (read
19
- // live from the active member row) nor the merged attributes bag, so the
20
- // client re-syncs from here (UpdateSession) after a session change such as
21
- // SetActiveOrganization.
22
- async function userHandler(c) {
23
- const context = c.get('lowdefyContext');
24
- return c.json({ user: context.user ?? null });
25
- }
26
-
27
- export default userHandler;
@@ -1,33 +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 { 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;
@@ -1,78 +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 { 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 DELETED
@@ -1,111 +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 '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
- }));
File without changes