@lowdefy/server-dev 4.6.0 → 4.7.1

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.
@@ -15,6 +15,7 @@
15
15
  */
16
16
 
17
17
  import React from 'react';
18
+ import { GenIcon } from 'react-icons/lib';
18
19
  import Client from '@lowdefy/client';
19
20
 
20
21
  import BuildErrorPage from './BuildErrorPage.js';
@@ -58,6 +59,17 @@ const Page = ({
58
59
  // Merge dynamic JS entries fetched after JIT build with the static jsMap
59
60
  const mergedJsMap = pageConfig._jsEntries ? { ...jsMap, ...pageConfig._jsEntries } : jsMap;
60
61
 
62
+ // Merge JIT-discovered icon data into the static icons object.
63
+ // createIcon.js looks up Icons[name] on every render from the captured reference,
64
+ // so mutating the original object makes new icons available immediately.
65
+ if (pageConfig._dynamicIcons) {
66
+ for (const [name, data] of Object.entries(pageConfig._dynamicIcons)) {
67
+ if (!types.icons[name]) {
68
+ types.icons[name] = GenIcon(data);
69
+ }
70
+ }
71
+ }
72
+
61
73
  return (
62
74
  <Client
63
75
  auth={auth}
@@ -32,6 +32,16 @@ async function fetchJsEntries(basePath) {
32
32
  }
33
33
  }
34
34
 
35
+ async function fetchDynamicIcons(basePath) {
36
+ try {
37
+ const res = await fetch(`${basePath}/api/icons/dynamic`);
38
+ if (!res.ok) return {};
39
+ return parseJsModule(await res.text());
40
+ } catch {
41
+ return {};
42
+ }
43
+ }
44
+
35
45
  async function fetchPageConfig(url) {
36
46
  const res = await fetch(url, {
37
47
  headers: { 'Content-Type': 'application/json' },
@@ -50,11 +60,16 @@ async function fetchPageConfig(url) {
50
60
  throw new Error(data.message || 'Request error');
51
61
  }
52
62
 
53
- // Fetch jsMap after page build completes (JIT build may have added new entries).
54
- // Extract basePath from the URL to construct the jsMap endpoint.
63
+ // Fetch jsMap and dynamic icons after page build completes
64
+ // (JIT build may have added new entries).
65
+ // Extract basePath from the URL to construct the endpoints.
55
66
  const basePath = url.replace(/\/api\/page\/.*$/, '');
56
- const jsEntries = await fetchJsEntries(basePath);
67
+ const [jsEntries, dynamicIcons] = await Promise.all([
68
+ fetchJsEntries(basePath),
69
+ fetchDynamicIcons(basePath),
70
+ ]);
57
71
  data._jsEntries = jsEntries;
72
+ data._dynamicIcons = dynamicIcons;
58
73
 
59
74
  return data;
60
75
  }
@@ -17,7 +17,7 @@
17
17
  import fs from 'fs';
18
18
  import path from 'path';
19
19
  import { serializer } from '@lowdefy/helpers';
20
- import { buildPageJit, createContext } from '@lowdefy/build/dev';
20
+ import { buildPageJit, createContext, makeId } from '@lowdefy/build/dev';
21
21
 
22
22
  import createLogger from './log/createLogger.js';
23
23
  import PageCache from './pageCache.mjs';
@@ -34,6 +34,12 @@ let cachedRegistry = null;
34
34
  let cachedBuildContext = null;
35
35
  let lastInvalidationMtime = null;
36
36
 
37
+ // Frozen snapshot of icon imports from the initial build (what's actually in the Next.js bundle).
38
+ // Module-level so it persists across context resets (skeleton rebuilds update iconImports.json
39
+ // with newly discovered icons, but those aren't in the bundle until the next nextBuild).
40
+ // Only resets when the server process restarts (which happens after every nextBuild).
41
+ let bundledIconImports = null;
42
+
37
43
  function readJsonFile(filePath) {
38
44
  try {
39
45
  const content = fs.readFileSync(filePath, 'utf8');
@@ -112,6 +118,25 @@ function getBuildContext(buildDirectory, configDirectory) {
112
118
  readJsonFile(path.join(buildDirectory, 'installedPluginPackages.json')) ?? [];
113
119
  cachedBuildContext.installedPluginPackages = new Set(installedPluginPackages);
114
120
 
121
+ // Use the frozen icon imports from the initial build for JIT detection.
122
+ // This represents what's actually in the Next.js bundle — not what shallowBuild
123
+ // discovers on subsequent rebuilds (those icons aren't bundled yet).
124
+ // bundledIconImports is module-level and only resets on server restart (after nextBuild).
125
+ if (!bundledIconImports) {
126
+ bundledIconImports = readJsonFile(path.join(buildDirectory, 'iconImports.json')) ?? [];
127
+ }
128
+ cachedBuildContext.iconImports = bundledIconImports;
129
+
130
+ // Accumulator for dynamically extracted icon SVG data written to plugins/iconsDynamic.js.
131
+ // Reset on skeleton rebuild (cachedBuildContext = null) — JIT re-discovers as needed.
132
+ cachedBuildContext.dynamicIconData = {};
133
+
134
+ // Advance makeId past all skeleton IDs to prevent collisions with JIT builds
135
+ const idCounter = readJsonFile(path.join(buildDirectory, 'idCounter.json'));
136
+ if (idCounter != null) {
137
+ makeId.setCounter(idCounter);
138
+ }
139
+
115
140
  return cachedBuildContext;
116
141
  }
117
142
 
package/manager/run.mjs CHANGED
@@ -19,7 +19,6 @@ import { wait } from '@lowdefy/helpers';
19
19
  import opener from 'opener';
20
20
  import getContext from './getContext.mjs';
21
21
  import startServer from './processes/startServer.mjs';
22
- import checkPortAvailable from './utils/checkPortAvailable.mjs';
23
22
 
24
23
  /*
25
24
  The run script does the following:
@@ -86,7 +85,6 @@ try {
86
85
  // because chokidar sometimes doesn't fire this event, and it seems like there isn't an issue with not waiting.
87
86
  context.startWatchers();
88
87
 
89
- await checkPortAvailable(context.options.port);
90
88
  startServer(context);
91
89
  await wait(800);
92
90
  if (process.env.LOWDEFY_SERVER_DEV_OPEN_BROWSER === 'true') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/server-dev",
3
- "version": "4.6.0",
3
+ "version": "4.7.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -36,34 +36,34 @@
36
36
  ".npmrc"
37
37
  ],
38
38
  "dependencies": {
39
- "@lowdefy/actions-core": "4.6.0",
40
- "@lowdefy/api": "4.6.0",
41
- "@lowdefy/block-utils": "4.6.0",
42
- "@lowdefy/blocks-aggrid": "4.6.0",
43
- "@lowdefy/blocks-antd": "4.6.0",
44
- "@lowdefy/blocks-basic": "4.6.0",
45
- "@lowdefy/blocks-color-selectors": "4.6.0",
46
- "@lowdefy/blocks-echarts": "4.6.0",
47
- "@lowdefy/blocks-loaders": "4.6.0",
48
- "@lowdefy/blocks-markdown": "4.6.0",
49
- "@lowdefy/blocks-qr": "4.6.0",
50
- "@lowdefy/build": "4.6.0",
51
- "@lowdefy/client": "4.6.0",
52
- "@lowdefy/engine": "4.6.0",
53
- "@lowdefy/errors": "4.6.0",
54
- "@lowdefy/helpers": "4.6.0",
55
- "@lowdefy/layout": "4.6.0",
56
- "@lowdefy/logger": "4.6.0",
57
- "@lowdefy/node-utils": "4.6.0",
58
- "@lowdefy/operators-change-case": "4.6.0",
59
- "@lowdefy/operators-diff": "4.6.0",
60
- "@lowdefy/operators-js": "4.6.0",
61
- "@lowdefy/operators-moment": "4.6.0",
62
- "@lowdefy/operators-mql": "4.6.0",
63
- "@lowdefy/operators-nunjucks": "4.6.0",
64
- "@lowdefy/operators-uuid": "4.6.0",
65
- "@lowdefy/operators-yaml": "4.6.0",
66
- "@lowdefy/plugin-next-auth": "4.6.0",
39
+ "@lowdefy/actions-core": "4.7.1",
40
+ "@lowdefy/api": "4.7.1",
41
+ "@lowdefy/block-utils": "4.7.1",
42
+ "@lowdefy/blocks-aggrid": "4.7.1",
43
+ "@lowdefy/blocks-antd": "4.7.1",
44
+ "@lowdefy/blocks-basic": "4.7.1",
45
+ "@lowdefy/blocks-color-selectors": "4.7.1",
46
+ "@lowdefy/blocks-echarts": "4.7.1",
47
+ "@lowdefy/blocks-loaders": "4.7.1",
48
+ "@lowdefy/blocks-markdown": "4.7.1",
49
+ "@lowdefy/blocks-qr": "4.7.1",
50
+ "@lowdefy/build": "4.7.1",
51
+ "@lowdefy/client": "4.7.1",
52
+ "@lowdefy/engine": "4.7.1",
53
+ "@lowdefy/errors": "4.7.1",
54
+ "@lowdefy/helpers": "4.7.1",
55
+ "@lowdefy/layout": "4.7.1",
56
+ "@lowdefy/logger": "4.7.1",
57
+ "@lowdefy/node-utils": "4.7.1",
58
+ "@lowdefy/operators-change-case": "4.7.1",
59
+ "@lowdefy/operators-diff": "4.7.1",
60
+ "@lowdefy/operators-js": "4.7.1",
61
+ "@lowdefy/operators-moment": "4.7.1",
62
+ "@lowdefy/operators-mql": "4.7.1",
63
+ "@lowdefy/operators-nunjucks": "4.7.1",
64
+ "@lowdefy/operators-uuid": "4.7.1",
65
+ "@lowdefy/operators-yaml": "4.7.1",
66
+ "@lowdefy/plugin-next-auth": "4.7.1",
67
67
  "chokidar": "3.5.3",
68
68
  "dotenv": "16.3.1",
69
69
  "next": "13.5.4",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/server-dev",
3
- "version": "4.6.0",
3
+ "version": "4.7.1",
4
4
  "license": "Apache-2.0",
5
5
  "description": "",
6
6
  "homepage": "https://lowdefy.com",
@@ -44,34 +44,34 @@
44
44
  "prepublishOnly": "pnpm build"
45
45
  },
46
46
  "dependencies": {
47
- "@lowdefy/actions-core": "4.6.0",
48
- "@lowdefy/api": "4.6.0",
49
- "@lowdefy/block-utils": "4.6.0",
50
- "@lowdefy/blocks-aggrid": "4.6.0",
51
- "@lowdefy/blocks-antd": "4.6.0",
52
- "@lowdefy/blocks-basic": "4.6.0",
53
- "@lowdefy/blocks-color-selectors": "4.6.0",
54
- "@lowdefy/blocks-echarts": "4.6.0",
55
- "@lowdefy/blocks-loaders": "4.6.0",
56
- "@lowdefy/blocks-markdown": "4.6.0",
57
- "@lowdefy/blocks-qr": "4.6.0",
58
- "@lowdefy/build": "4.6.0",
59
- "@lowdefy/client": "4.6.0",
60
- "@lowdefy/engine": "4.6.0",
61
- "@lowdefy/errors": "4.6.0",
62
- "@lowdefy/helpers": "4.6.0",
63
- "@lowdefy/layout": "4.6.0",
64
- "@lowdefy/logger": "4.6.0",
65
- "@lowdefy/node-utils": "4.6.0",
66
- "@lowdefy/operators-change-case": "4.6.0",
67
- "@lowdefy/operators-diff": "4.6.0",
68
- "@lowdefy/operators-js": "4.6.0",
69
- "@lowdefy/operators-moment": "4.6.0",
70
- "@lowdefy/operators-mql": "4.6.0",
71
- "@lowdefy/operators-nunjucks": "4.6.0",
72
- "@lowdefy/operators-uuid": "4.6.0",
73
- "@lowdefy/operators-yaml": "4.6.0",
74
- "@lowdefy/plugin-next-auth": "4.6.0",
47
+ "@lowdefy/actions-core": "4.7.1",
48
+ "@lowdefy/api": "4.7.1",
49
+ "@lowdefy/block-utils": "4.7.1",
50
+ "@lowdefy/blocks-aggrid": "4.7.1",
51
+ "@lowdefy/blocks-antd": "4.7.1",
52
+ "@lowdefy/blocks-basic": "4.7.1",
53
+ "@lowdefy/blocks-color-selectors": "4.7.1",
54
+ "@lowdefy/blocks-echarts": "4.7.1",
55
+ "@lowdefy/blocks-loaders": "4.7.1",
56
+ "@lowdefy/blocks-markdown": "4.7.1",
57
+ "@lowdefy/blocks-qr": "4.7.1",
58
+ "@lowdefy/build": "4.7.1",
59
+ "@lowdefy/client": "4.7.1",
60
+ "@lowdefy/engine": "4.7.1",
61
+ "@lowdefy/errors": "4.7.1",
62
+ "@lowdefy/helpers": "4.7.1",
63
+ "@lowdefy/layout": "4.7.1",
64
+ "@lowdefy/logger": "4.7.1",
65
+ "@lowdefy/node-utils": "4.7.1",
66
+ "@lowdefy/operators-change-case": "4.7.1",
67
+ "@lowdefy/operators-diff": "4.7.1",
68
+ "@lowdefy/operators-js": "4.7.1",
69
+ "@lowdefy/operators-moment": "4.7.1",
70
+ "@lowdefy/operators-mql": "4.7.1",
71
+ "@lowdefy/operators-nunjucks": "4.7.1",
72
+ "@lowdefy/operators-uuid": "4.7.1",
73
+ "@lowdefy/operators-yaml": "4.7.1",
74
+ "@lowdefy/plugin-next-auth": "4.7.1",
75
75
  "chokidar": "3.5.3",
76
76
  "dotenv": "16.3.1",
77
77
  "next": "13.5.4",
@@ -0,0 +1,34 @@
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
+
20
+ async function handler(req, res) {
21
+ const filePath = path.join(process.cwd(), 'build', 'plugins', 'iconsDynamic.js');
22
+
23
+ try {
24
+ const content = fs.readFileSync(filePath, 'utf8');
25
+ res.setHeader('Content-Type', 'application/javascript');
26
+ res.status(200).send(content);
27
+ } catch {
28
+ // Return empty default export if file doesn't exist yet
29
+ res.setHeader('Content-Type', 'application/javascript');
30
+ res.status(200).send('export default {};');
31
+ }
32
+ }
33
+
34
+ export default handler;
@@ -1,41 +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 net from 'net';
19
-
20
- function checkPortAvailable(port) {
21
- return new Promise((resolve, reject) => {
22
- const server = net.createServer();
23
- server.once('error', (err) => {
24
- if (err.code === 'EADDRINUSE') {
25
- reject(
26
- new BuildError(
27
- `Port ${port} is already in use. Stop the other process or use a different port with --port.`
28
- )
29
- );
30
- } else {
31
- reject(err);
32
- }
33
- });
34
- server.once('listening', () => {
35
- server.close(() => resolve());
36
- });
37
- server.listen(port);
38
- });
39
- }
40
-
41
- export default checkPortAvailable;