@aaqu/fromcubes-portal-react 0.1.0-alpha.24 → 0.1.0-alpha.25

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.
package/README.md CHANGED
@@ -227,7 +227,8 @@ Import **Shared Components** first — it provides the UI building blocks (Page,
227
227
  | Red status "legacy endpoint" on deploy | Flow was saved before the `/fromcubes/` prefix became hardcoded. Open the node, set **Sub-path**, redeploy. Automatic migration is disabled to avoid silent URL changes. |
228
228
  | Red status "bad sub-path" | Sub-path is empty or violates the rules (no leading `/`, no whitespace, no `..`, segments must start alphanumerically, `public`/`_ws` reserved). |
229
229
  | Yellow status "css-fail" | Tailwind generation failed (usually an invalid class in JSX). Page still loads but unstyled. Fix the class, redeploy — the status clears on the next successful build. |
230
- | `npm install @aaqu/fromcubes-portal-react` ends with `EACCES` on `postinstall` | `scripts/sync-resources.js` copies Monaco into `resources/vs/` after install. The Node-RED `userDir/node_modules/...` install path must be writable by the user running Node-RED. Re-run `npm install` from a shell with the right ownership. |
230
+ | Code editor stays blank / `portal-react/vs/loader.js` 404s | Monaco is served live from the package's own `node_modules` at `/portal-react/vs` (no postinstall copy step). The 404 means `RED.httpAdmin` is mounted under a non-root `httpAdminRoot` the editor didn't account for, or `monaco-editor` failed to install. Reinstall the package and hard-refresh the editor (`Cmd/Ctrl-Shift-R`). |
231
+ | `npm install @aaqu/fromcubes-portal-react` ends with `EACCES` | The Node-RED `userDir/node_modules` install path is not writable by the user running Node-RED. Re-run `npm install` from a shell with the right ownership. |
231
232
  | Browser request `/portal-react/css/<hash>.css` returns 404 | The portal's deploy hasn't produced a CSS bundle yet — open the editor, redeploy. If the URL is bookmarked from before a deploy, the hash is stale; reload the portal page itself. |
232
233
  | WebSocket reconnects in an endless loop | Reverse-proxy is not forwarding `Upgrade: websocket` on `/fromcubes/<sub-path>/_ws`. Check the proxy config — nginx needs `proxy_set_header Upgrade $http_upgrade`, Traefik needs the `websocket` middleware. |
233
234
  | `libs` packages fail to install on deploy | The user-installed npm packages declared in **Libs** install via Node-RED's `dynamicModuleList` mechanism, which needs network access from `userDir` and a writable `node_modules`. Behind a corporate proxy set `npm config set proxy …` for the Node-RED user. |
@@ -387,6 +387,20 @@ function serveableHash(state) {
387
387
  return state.compiled.js ? state.contentHash || "" : "";
388
388
  }
389
389
 
390
+ /**
391
+ * True only when `state` holds a real, serveable build. Used by the no-op
392
+ * redeploy guard to decide whether a rebuild can be skipped. MUST be false
393
+ * after close() nulls `compiled` (`{ ...compiled: null }` teardown window),
394
+ * otherwise the guard treats the destroyed build as valid, skips the rebuild,
395
+ * and the GET route serves the holding page forever (permanent spinner).
396
+ *
397
+ * @param {?PageState} state pageState[endpoint] (may be null/torn down).
398
+ * @returns {boolean}
399
+ */
400
+ function hasFreshBuild(state) {
401
+ return !!state && !state.building && !!state.compiled && !state.compiled.error;
402
+ }
403
+
390
404
  module.exports = function (RED) {
391
405
  return createHelpers(RED);
392
406
  };
@@ -398,6 +412,7 @@ module.exports.formatEsbuildError = formatEsbuildError;
398
412
  module.exports.extractPortalUser = extractPortalUser;
399
413
  module.exports.findMissingComponentRefs = findMissingComponentRefs;
400
414
  module.exports.serveableHash = serveableHash;
415
+ module.exports.hasFreshBuild = hasFreshBuild;
401
416
  module.exports.NAME_MAX_LEN = NAME_MAX_LEN;
402
417
  module.exports.MAX_GROUPS_HEADER_BYTES = MAX_GROUPS_HEADER_BYTES;
403
418
 
@@ -619,6 +634,7 @@ function createHelpers(RED) {
619
634
  generateCSS,
620
635
  extractPortalUser,
621
636
  serveableHash,
637
+ hasFreshBuild,
622
638
  removeRoute,
623
639
  isSafeName,
624
640
  validateSubPath,
@@ -545,6 +545,7 @@ module.exports = function (RED) {
545
545
  generateCSS,
546
546
  extractPortalUser,
547
547
  serveableHash,
548
+ hasFreshBuild,
548
549
  removeRoute,
549
550
  isSafeName,
550
551
  validateSubPath,
@@ -1549,8 +1550,11 @@ module.exports = function (RED) {
1549
1550
  );
1550
1551
  const prevSig = portalSig[nodeId];
1551
1552
  const existing = pageState[endpoint];
1552
- const hasValidBuild =
1553
- !!existing && !existing.building && !existing.compiled?.error;
1553
+ // hasFreshBuild also requires `compiled` to be present — close() nulls it on
1554
+ // redeploy, and a guard that ignored that (checking only building/error)
1555
+ // would treat the destroyed build as valid and skip the rebuild, leaving the
1556
+ // GET route serving the holding page forever. See helpers.hasFreshBuild.
1557
+ const hasValidBuild = hasFreshBuild(existing);
1554
1558
  portalSig[nodeId] = sig;
1555
1559
 
1556
1560
  if (prevSig !== sig || !hasValidBuild) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aaqu/fromcubes-portal-react",
3
- "version": "0.1.0-alpha.24",
3
+ "version": "0.1.0-alpha.25",
4
4
  "description": "Fromcubes Portal - React for Node-RED with Tailwind CSS and auto complete",
5
5
  "type": "commonjs",
6
6
  "keywords": [