@valbuild/next 0.96.3 → 0.97.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.
Files changed (56) hide show
  1. package/README.md +33 -0
  2. package/client/dist/valbuild-next-client.cjs.dev.js +29 -10
  3. package/client/dist/valbuild-next-client.cjs.prod.js +29 -10
  4. package/client/dist/valbuild-next-client.esm.js +29 -10
  5. package/dist/{ValApp-3df36e0d.cjs.dev.js → ValApp-1992d8b3.cjs.dev.js} +3 -2
  6. package/dist/{ValApp-e2e1dfed.esm.js → ValApp-8912fc8b.esm.js} +3 -2
  7. package/dist/ValApp-bb207e96.cjs.js +7 -0
  8. package/dist/{ValApp-22147086.cjs.prod.js → ValApp-bb207e96.cjs.prod.js} +3 -2
  9. package/dist/ValModulesClient-0dd601a1.esm.js +48 -0
  10. package/dist/ValModulesClient-10c0721c.cjs.js +7 -0
  11. package/dist/ValModulesClient-10c0721c.cjs.prod.js +53 -0
  12. package/dist/ValModulesClient-4ecabf99.cjs.dev.js +53 -0
  13. package/dist/{ValNextProvider-26ef0c1c.cjs.dev.js → ValNextProvider-03a9f8fd.cjs.dev.js} +103 -25
  14. package/dist/ValNextProvider-6103cb74.cjs.js +7 -0
  15. package/dist/{ValNextProvider-b0d9fa24.cjs.prod.js → ValNextProvider-6103cb74.cjs.prod.js} +103 -25
  16. package/dist/{ValNextProvider-e163e127.esm.js → ValNextProvider-7302b8af.esm.js} +104 -26
  17. package/dist/ValOverlayContext-3c37e5a7.esm.js +251 -0
  18. package/dist/ValOverlayContext-c6f27a6d.cjs.dev.js +262 -0
  19. package/dist/ValOverlayContext-f7f45bc7.cjs.js +7 -0
  20. package/dist/ValOverlayContext-f7f45bc7.cjs.prod.js +262 -0
  21. package/dist/declarations/src/ValApp.d.ts +3 -1
  22. package/dist/declarations/src/ValModulesClient.d.ts +38 -0
  23. package/dist/declarations/src/ValProvider.d.ts +1 -0
  24. package/dist/declarations/src/external_exempt_from_val_quickjs.d.ts +1 -0
  25. package/dist/declarations/src/initVal.d.ts +25 -0
  26. package/dist/defineProperty-8951f469.cjs.prod.js +29 -0
  27. package/dist/defineProperty-cca5affa.esm.js +26 -0
  28. package/dist/defineProperty-f90345d7.cjs.dev.js +29 -0
  29. package/dist/{objectSpread2-3c87fb4f.cjs.prod.js → objectSpread2-13f847a9.cjs.prod.js} +2 -27
  30. package/dist/{objectSpread2-792eb2c2.cjs.dev.js → objectSpread2-58024783.cjs.dev.js} +2 -27
  31. package/dist/{objectSpread2-c1340c1c.esm.js → objectSpread2-60d1bd93.esm.js} +2 -25
  32. package/dist/{routeFromVal-ef8c304a.cjs.prod.js → routeFromVal-13c832b2.cjs.prod.js} +1 -1
  33. package/dist/{routeFromVal-748d2aec.esm.js → routeFromVal-8855a5cc.esm.js} +1 -1
  34. package/dist/{routeFromVal-8fdaa0d0.cjs.dev.js → routeFromVal-9b610e77.cjs.dev.js} +1 -1
  35. package/dist/valbuild-next.cjs.dev.js +56 -4
  36. package/dist/valbuild-next.cjs.prod.js +56 -4
  37. package/dist/valbuild-next.esm.js +48 -4
  38. package/dist/{asyncToGenerator-8e5c36c8.cjs.prod.js → version-0e6da9a7.cjs.prod.js} +10 -0
  39. package/dist/{asyncToGenerator-500f022f.esm.js → version-4d7b692c.esm.js} +10 -1
  40. package/dist/{asyncToGenerator-c3823d62.cjs.dev.js → version-c311d818.cjs.dev.js} +10 -0
  41. package/package.json +14 -8
  42. package/rsc/dist/valbuild-next-rsc.cjs.dev.js +38 -36
  43. package/rsc/dist/valbuild-next-rsc.cjs.prod.js +38 -36
  44. package/rsc/dist/valbuild-next-rsc.esm.js +7 -5
  45. package/server/dist/valbuild-next-server.cjs.dev.js +9 -9
  46. package/server/dist/valbuild-next-server.cjs.prod.js +9 -9
  47. package/server/dist/valbuild-next-server.esm.js +3 -3
  48. package/dist/ValApp-22147086.cjs.js +0 -7
  49. package/dist/ValNextProvider-b0d9fa24.cjs.js +0 -7
  50. package/dist/ValOverlayContext-6635a4d7.esm.js +0 -114
  51. package/dist/ValOverlayContext-942f1294.cjs.js +0 -7
  52. package/dist/ValOverlayContext-942f1294.cjs.prod.js +0 -125
  53. package/dist/ValOverlayContext-c82a4507.cjs.dev.js +0 -125
  54. package/dist/version-16f6b0ce.esm.js +0 -10
  55. package/dist/version-d034b2fc.cjs.dev.js +0 -12
  56. package/dist/version-d2061026.cjs.prod.js +0 -12
@@ -4,18 +4,20 @@
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
6
  var slicedToArray = require('./slicedToArray-44036a76.cjs.dev.js');
7
- var objectSpread2 = require('./objectSpread2-792eb2c2.cjs.dev.js');
7
+ var objectSpread2 = require('./objectSpread2-58024783.cjs.dev.js');
8
8
  var core = require('@valbuild/core');
9
9
  var ui = require('@valbuild/ui');
10
10
  var navigation = require('next/navigation');
11
11
  var Script = require('next/script');
12
12
  var React = require('react');
13
- var ValOverlayContext = require('./ValOverlayContext-c82a4507.cjs.dev.js');
13
+ var ValOverlayContext = require('./ValOverlayContext-c6f27a6d.cjs.dev.js');
14
14
  var stega = require('@valbuild/react/stega');
15
15
  var internal = require('@valbuild/shared/internal');
16
16
  var cssUtils = require('./cssUtils-9c11102f.cjs.dev.js');
17
+ var createForOfIteratorHelper = require('./createForOfIteratorHelper-0445603c.cjs.dev.js');
17
18
  var jsxRuntime = require('react/jsx-runtime');
18
19
  require('./unsupportedIterableToArray-c8ab77c9.cjs.dev.js');
20
+ require('./defineProperty-f90345d7.cjs.dev.js');
19
21
 
20
22
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
21
23
 
@@ -41,6 +43,39 @@ function initSessionTheme(config) {
41
43
  return theme;
42
44
  }
43
45
 
46
+ /**
47
+ * Returns true if the Val Enable cookie is set to "true" in the given cookie
48
+ * string (typically `document.cookie`).
49
+ *
50
+ * Exact-token match: a naive `includes("val_enable=true")` would also match
51
+ * unrelated cookies like `xval_enable=true`. The server sets the cookie to
52
+ * "false" (rather than deleting it) on disable, so the value must be checked
53
+ * too.
54
+ */
55
+ function hasValEnableCookie(cookieString) {
56
+ var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(cookieString.split(";")),
57
+ _step;
58
+ try {
59
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
60
+ var part = _step.value;
61
+ var eq = part.indexOf("=");
62
+ if (eq === -1) {
63
+ continue;
64
+ }
65
+ var name = part.slice(0, eq).trim();
66
+ var value = part.slice(eq + 1).trim();
67
+ if (name === core.Internal.VAL_ENABLE_COOKIE_NAME && value === "true") {
68
+ return true;
69
+ }
70
+ }
71
+ } catch (err) {
72
+ _iterator.e(err);
73
+ } finally {
74
+ _iterator.f();
75
+ }
76
+ return false;
77
+ }
78
+
44
79
  var ValNextProvider = function ValNextProvider(props) {
45
80
  // TODO: use config:
46
81
  var route = "/api/val";
@@ -54,27 +89,40 @@ var ValNextProvider = function ValNextProvider(props) {
54
89
  var valStore = React__default["default"].useMemo(function () {
55
90
  return new ValOverlayContext.ValExternalStore();
56
91
  }, []);
57
- var _React$useState = React__default["default"].useState(),
92
+ // Whether useValStega should actually suspend. False during SSR and the
93
+ // hydration render — the server store is never populated (draft data
94
+ // arrives via browser CustomEvents only), so suspending there would just
95
+ // stall into the waitForLoad timeout, and hydration must render the static
96
+ // source so it matches the server HTML exactly. Activated post-hydration
97
+ // (in an effect, only when the Val Enable cookie is present) inside a
98
+ // transition: React keeps the static content visible while hooks suspend
99
+ // and then swaps to draft data as a normal update — no Suspense fallback
100
+ // flash and no hydration mismatch.
101
+ var _React$useState = React__default["default"].useState(false),
58
102
  _React$useState2 = slicedToArray._slicedToArray(_React$useState, 2),
59
- mountOverlay = _React$useState2[0],
60
- setMountOverlay = _React$useState2[1];
61
- var _React$useState3 = React__default["default"].useState(null),
103
+ suspendActive = _React$useState2[0],
104
+ setSuspendActive = _React$useState2[1];
105
+ var _React$useState3 = React__default["default"].useState(),
62
106
  _React$useState4 = slicedToArray._slicedToArray(_React$useState3, 2),
63
- draftMode = _React$useState4[0],
64
- setDraftMode = _React$useState4[1];
65
- var _React$useState5 = React__default["default"].useState(false),
107
+ mountOverlay = _React$useState4[0],
108
+ setMountOverlay = _React$useState4[1];
109
+ var _React$useState5 = React__default["default"].useState(null),
66
110
  _React$useState6 = slicedToArray._slicedToArray(_React$useState5, 2),
67
- spaReady = _React$useState6[0],
68
- setSpaReady = _React$useState6[1]; // TODO: consider removing spaReady - it is not used? If we remove, clean up the custom events that send the message too...
111
+ draftMode = _React$useState6[0],
112
+ setDraftMode = _React$useState6[1];
113
+ var _React$useState7 = React__default["default"].useState(false),
114
+ _React$useState8 = slicedToArray._slicedToArray(_React$useState7, 2),
115
+ spaReady = _React$useState8[0],
116
+ setSpaReady = _React$useState8[1]; // TODO: consider removing spaReady - it is not used? If we remove, clean up the custom events that send the message too...
69
117
  var router = navigation.useRouter();
70
118
  var _React$useTransition = React__default["default"].useTransition(),
71
119
  _React$useTransition2 = slicedToArray._slicedToArray(_React$useTransition, 2),
72
120
  startTransition = _React$useTransition2[1];
73
121
  var rerenderCounterRef = React__default["default"].useRef(0);
74
- var _React$useState7 = React__default["default"].useState(null),
75
- _React$useState8 = slicedToArray._slicedToArray(_React$useState7, 2),
76
- iframeSrc = _React$useState8[0],
77
- setIframeSrc = _React$useState8[1];
122
+ var _React$useState9 = React__default["default"].useState(null),
123
+ _React$useState0 = slicedToArray._slicedToArray(_React$useState9, 2),
124
+ iframeSrc = _React$useState0[0],
125
+ setIframeSrc = _React$useState0[1];
78
126
  var pathname = navigation.usePathname();
79
127
  React.useEffect(function () {
80
128
  window.dispatchEvent(new CustomEvent("val-provider:pathname", {
@@ -91,8 +139,21 @@ var ValNextProvider = function ValNextProvider(props) {
91
139
  setMountOverlay(false);
92
140
  return;
93
141
  }
94
- setMountOverlay(document.cookie.includes("".concat(core.Internal.VAL_ENABLE_COOKIE_NAME, "=true")));
142
+ setMountOverlay(hasValEnableCookie(document.cookie));
95
143
  }, []);
144
+ React__default["default"].useEffect(function () {
145
+ // Activate the Suspense gate after hydration. Inside a transition so
146
+ // already-visible (static) content stays on screen while useValStega
147
+ // suspends — no fallback flash — and the swap to draft data commits as a
148
+ // normal update instead of a hydration mismatch. Never deactivated:
149
+ // components must not stop suspending across renders (the draft-mode-off
150
+ // release valve lives in useValStega instead).
151
+ if (props.suspend && shouldEnableVal()) {
152
+ startTransition(function () {
153
+ setSuspendActive(true);
154
+ });
155
+ }
156
+ }, [props.suspend]);
96
157
  React__default["default"].useEffect(function () {
97
158
  if (!mountOverlay) {
98
159
  return;
@@ -179,7 +240,11 @@ var ValNextProvider = function ValNextProvider(props) {
179
240
  return;
180
241
  }
181
242
  if (res.status === 401) {
182
- // ignore when not authorized
243
+ // Not authorized (e.g. stale Val Enable cookie after the session
244
+ // expired): treat draft mode as off so useValStega's Suspense gate
245
+ // is released instead of leaving draftMode stuck at null and
246
+ // re-suspending into the waitForLoad timeout.
247
+ setDraftMode(false);
183
248
  return;
184
249
  }
185
250
  if (res.status !== 200) {
@@ -275,10 +340,10 @@ var ValNextProvider = function ValNextProvider(props) {
275
340
  window.dispatchEvent(new CustomEvent("val-append-overlay"));
276
341
  }
277
342
  });
278
- var _React$useState9 = React__default["default"].useState(null),
279
- _React$useState0 = slicedToArray._slicedToArray(_React$useState9, 2),
280
- dropZone = _React$useState0[0],
281
- setDropZone = _React$useState0[1];
343
+ var _React$useState1 = React__default["default"].useState(null),
344
+ _React$useState10 = slicedToArray._slicedToArray(_React$useState1, 2),
345
+ dropZone = _React$useState10[0],
346
+ setDropZone = _React$useState10[1];
282
347
  React__default["default"].useEffect(function () {
283
348
  var storedDropZone = localStorage.getItem("val-menu-drop-zone-default");
284
349
  if (storedDropZone) {
@@ -291,10 +356,10 @@ var ValNextProvider = function ValNextProvider(props) {
291
356
  var initTheme = React__default["default"].useMemo(function () {
292
357
  return initSessionTheme(props.config);
293
358
  }, [props.config]);
294
- var _React$useState1 = React__default["default"].useState(false),
295
- _React$useState10 = slicedToArray._slicedToArray(_React$useState1, 2),
296
- spaLoaded = _React$useState10[0],
297
- setSpaLoaded = _React$useState10[1];
359
+ var _React$useState11 = React__default["default"].useState(false),
360
+ _React$useState12 = slicedToArray._slicedToArray(_React$useState11, 2),
361
+ spaLoaded = _React$useState12[0],
362
+ setSpaLoaded = _React$useState12[1];
298
363
  React__default["default"].useEffect(function () {
299
364
  var listener = function listener() {
300
365
  setSpaLoaded(true);
@@ -334,6 +399,7 @@ var ValNextProvider = function ValNextProvider(props) {
334
399
  }, [cssUtils.valPrefixedClass]);
335
400
  return /*#__PURE__*/jsxRuntime.jsxs(ValOverlayContext.ValOverlayProvider, {
336
401
  draftMode: draftMode,
402
+ suspend: suspendActive,
337
403
  store: valStore,
338
404
  children: [props.children, dropZone !== null && !spaLoaded && mountOverlay && initTheme !== null && /*#__PURE__*/jsxRuntime.jsxs(React__default["default"].Fragment, {
339
405
  children: [/*#__PURE__*/jsxRuntime.jsx("style", {
@@ -447,6 +513,18 @@ function isValStudioPath(pathname) {
447
513
  return pathname.startsWith("/val");
448
514
  }
449
515
 
516
+ // Same guards as the mountOverlay effect: suspending where the overlay can't
517
+ // mount would only ever stall into the waitForLoad timeout. Browser-only.
518
+ function shouldEnableVal() {
519
+ if (location.search === "?message_onready=true") {
520
+ return false;
521
+ }
522
+ if (isValStudioPath(location.pathname)) {
523
+ return false;
524
+ }
525
+ return hasValEnableCookie(document.cookie);
526
+ }
527
+
450
528
  // function ValIcon() {
451
529
  // return (
452
530
  // <svg
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === "production") {
4
+ module.exports = require("./ValNextProvider-6103cb74.cjs.prod.js");
5
+ } else {
6
+ module.exports = require("./ValNextProvider-6103cb74.cjs.dev.js");
7
+ }
@@ -4,18 +4,20 @@
4
4
  Object.defineProperty(exports, '__esModule', { value: true });
5
5
 
6
6
  var slicedToArray = require('./slicedToArray-ce613de6.cjs.prod.js');
7
- var objectSpread2 = require('./objectSpread2-3c87fb4f.cjs.prod.js');
7
+ var objectSpread2 = require('./objectSpread2-13f847a9.cjs.prod.js');
8
8
  var core = require('@valbuild/core');
9
9
  var ui = require('@valbuild/ui');
10
10
  var navigation = require('next/navigation');
11
11
  var Script = require('next/script');
12
12
  var React = require('react');
13
- var ValOverlayContext = require('./ValOverlayContext-942f1294.cjs.prod.js');
13
+ var ValOverlayContext = require('./ValOverlayContext-f7f45bc7.cjs.prod.js');
14
14
  var stega = require('@valbuild/react/stega');
15
15
  var internal = require('@valbuild/shared/internal');
16
16
  var cssUtils = require('./cssUtils-d97a757c.cjs.prod.js');
17
+ var createForOfIteratorHelper = require('./createForOfIteratorHelper-d4afcad8.cjs.prod.js');
17
18
  var jsxRuntime = require('react/jsx-runtime');
18
19
  require('./unsupportedIterableToArray-0d2087a2.cjs.prod.js');
20
+ require('./defineProperty-8951f469.cjs.prod.js');
19
21
 
20
22
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
21
23
 
@@ -41,6 +43,39 @@ function initSessionTheme(config) {
41
43
  return theme;
42
44
  }
43
45
 
46
+ /**
47
+ * Returns true if the Val Enable cookie is set to "true" in the given cookie
48
+ * string (typically `document.cookie`).
49
+ *
50
+ * Exact-token match: a naive `includes("val_enable=true")` would also match
51
+ * unrelated cookies like `xval_enable=true`. The server sets the cookie to
52
+ * "false" (rather than deleting it) on disable, so the value must be checked
53
+ * too.
54
+ */
55
+ function hasValEnableCookie(cookieString) {
56
+ var _iterator = createForOfIteratorHelper._createForOfIteratorHelper(cookieString.split(";")),
57
+ _step;
58
+ try {
59
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
60
+ var part = _step.value;
61
+ var eq = part.indexOf("=");
62
+ if (eq === -1) {
63
+ continue;
64
+ }
65
+ var name = part.slice(0, eq).trim();
66
+ var value = part.slice(eq + 1).trim();
67
+ if (name === core.Internal.VAL_ENABLE_COOKIE_NAME && value === "true") {
68
+ return true;
69
+ }
70
+ }
71
+ } catch (err) {
72
+ _iterator.e(err);
73
+ } finally {
74
+ _iterator.f();
75
+ }
76
+ return false;
77
+ }
78
+
44
79
  var ValNextProvider = function ValNextProvider(props) {
45
80
  // TODO: use config:
46
81
  var route = "/api/val";
@@ -54,27 +89,40 @@ var ValNextProvider = function ValNextProvider(props) {
54
89
  var valStore = React__default["default"].useMemo(function () {
55
90
  return new ValOverlayContext.ValExternalStore();
56
91
  }, []);
57
- var _React$useState = React__default["default"].useState(),
92
+ // Whether useValStega should actually suspend. False during SSR and the
93
+ // hydration render — the server store is never populated (draft data
94
+ // arrives via browser CustomEvents only), so suspending there would just
95
+ // stall into the waitForLoad timeout, and hydration must render the static
96
+ // source so it matches the server HTML exactly. Activated post-hydration
97
+ // (in an effect, only when the Val Enable cookie is present) inside a
98
+ // transition: React keeps the static content visible while hooks suspend
99
+ // and then swaps to draft data as a normal update — no Suspense fallback
100
+ // flash and no hydration mismatch.
101
+ var _React$useState = React__default["default"].useState(false),
58
102
  _React$useState2 = slicedToArray._slicedToArray(_React$useState, 2),
59
- mountOverlay = _React$useState2[0],
60
- setMountOverlay = _React$useState2[1];
61
- var _React$useState3 = React__default["default"].useState(null),
103
+ suspendActive = _React$useState2[0],
104
+ setSuspendActive = _React$useState2[1];
105
+ var _React$useState3 = React__default["default"].useState(),
62
106
  _React$useState4 = slicedToArray._slicedToArray(_React$useState3, 2),
63
- draftMode = _React$useState4[0],
64
- setDraftMode = _React$useState4[1];
65
- var _React$useState5 = React__default["default"].useState(false),
107
+ mountOverlay = _React$useState4[0],
108
+ setMountOverlay = _React$useState4[1];
109
+ var _React$useState5 = React__default["default"].useState(null),
66
110
  _React$useState6 = slicedToArray._slicedToArray(_React$useState5, 2),
67
- spaReady = _React$useState6[0],
68
- setSpaReady = _React$useState6[1]; // TODO: consider removing spaReady - it is not used? If we remove, clean up the custom events that send the message too...
111
+ draftMode = _React$useState6[0],
112
+ setDraftMode = _React$useState6[1];
113
+ var _React$useState7 = React__default["default"].useState(false),
114
+ _React$useState8 = slicedToArray._slicedToArray(_React$useState7, 2),
115
+ spaReady = _React$useState8[0],
116
+ setSpaReady = _React$useState8[1]; // TODO: consider removing spaReady - it is not used? If we remove, clean up the custom events that send the message too...
69
117
  var router = navigation.useRouter();
70
118
  var _React$useTransition = React__default["default"].useTransition(),
71
119
  _React$useTransition2 = slicedToArray._slicedToArray(_React$useTransition, 2),
72
120
  startTransition = _React$useTransition2[1];
73
121
  var rerenderCounterRef = React__default["default"].useRef(0);
74
- var _React$useState7 = React__default["default"].useState(null),
75
- _React$useState8 = slicedToArray._slicedToArray(_React$useState7, 2),
76
- iframeSrc = _React$useState8[0],
77
- setIframeSrc = _React$useState8[1];
122
+ var _React$useState9 = React__default["default"].useState(null),
123
+ _React$useState0 = slicedToArray._slicedToArray(_React$useState9, 2),
124
+ iframeSrc = _React$useState0[0],
125
+ setIframeSrc = _React$useState0[1];
78
126
  var pathname = navigation.usePathname();
79
127
  React.useEffect(function () {
80
128
  window.dispatchEvent(new CustomEvent("val-provider:pathname", {
@@ -91,8 +139,21 @@ var ValNextProvider = function ValNextProvider(props) {
91
139
  setMountOverlay(false);
92
140
  return;
93
141
  }
94
- setMountOverlay(document.cookie.includes("".concat(core.Internal.VAL_ENABLE_COOKIE_NAME, "=true")));
142
+ setMountOverlay(hasValEnableCookie(document.cookie));
95
143
  }, []);
144
+ React__default["default"].useEffect(function () {
145
+ // Activate the Suspense gate after hydration. Inside a transition so
146
+ // already-visible (static) content stays on screen while useValStega
147
+ // suspends — no fallback flash — and the swap to draft data commits as a
148
+ // normal update instead of a hydration mismatch. Never deactivated:
149
+ // components must not stop suspending across renders (the draft-mode-off
150
+ // release valve lives in useValStega instead).
151
+ if (props.suspend && shouldEnableVal()) {
152
+ startTransition(function () {
153
+ setSuspendActive(true);
154
+ });
155
+ }
156
+ }, [props.suspend]);
96
157
  React__default["default"].useEffect(function () {
97
158
  if (!mountOverlay) {
98
159
  return;
@@ -179,7 +240,11 @@ var ValNextProvider = function ValNextProvider(props) {
179
240
  return;
180
241
  }
181
242
  if (res.status === 401) {
182
- // ignore when not authorized
243
+ // Not authorized (e.g. stale Val Enable cookie after the session
244
+ // expired): treat draft mode as off so useValStega's Suspense gate
245
+ // is released instead of leaving draftMode stuck at null and
246
+ // re-suspending into the waitForLoad timeout.
247
+ setDraftMode(false);
183
248
  return;
184
249
  }
185
250
  if (res.status !== 200) {
@@ -275,10 +340,10 @@ var ValNextProvider = function ValNextProvider(props) {
275
340
  window.dispatchEvent(new CustomEvent("val-append-overlay"));
276
341
  }
277
342
  });
278
- var _React$useState9 = React__default["default"].useState(null),
279
- _React$useState0 = slicedToArray._slicedToArray(_React$useState9, 2),
280
- dropZone = _React$useState0[0],
281
- setDropZone = _React$useState0[1];
343
+ var _React$useState1 = React__default["default"].useState(null),
344
+ _React$useState10 = slicedToArray._slicedToArray(_React$useState1, 2),
345
+ dropZone = _React$useState10[0],
346
+ setDropZone = _React$useState10[1];
282
347
  React__default["default"].useEffect(function () {
283
348
  var storedDropZone = localStorage.getItem("val-menu-drop-zone-default");
284
349
  if (storedDropZone) {
@@ -291,10 +356,10 @@ var ValNextProvider = function ValNextProvider(props) {
291
356
  var initTheme = React__default["default"].useMemo(function () {
292
357
  return initSessionTheme(props.config);
293
358
  }, [props.config]);
294
- var _React$useState1 = React__default["default"].useState(false),
295
- _React$useState10 = slicedToArray._slicedToArray(_React$useState1, 2),
296
- spaLoaded = _React$useState10[0],
297
- setSpaLoaded = _React$useState10[1];
359
+ var _React$useState11 = React__default["default"].useState(false),
360
+ _React$useState12 = slicedToArray._slicedToArray(_React$useState11, 2),
361
+ spaLoaded = _React$useState12[0],
362
+ setSpaLoaded = _React$useState12[1];
298
363
  React__default["default"].useEffect(function () {
299
364
  var listener = function listener() {
300
365
  setSpaLoaded(true);
@@ -334,6 +399,7 @@ var ValNextProvider = function ValNextProvider(props) {
334
399
  }, [cssUtils.valPrefixedClass]);
335
400
  return /*#__PURE__*/jsxRuntime.jsxs(ValOverlayContext.ValOverlayProvider, {
336
401
  draftMode: draftMode,
402
+ suspend: suspendActive,
337
403
  store: valStore,
338
404
  children: [props.children, dropZone !== null && !spaLoaded && mountOverlay && initTheme !== null && /*#__PURE__*/jsxRuntime.jsxs(React__default["default"].Fragment, {
339
405
  children: [/*#__PURE__*/jsxRuntime.jsx("style", {
@@ -447,6 +513,18 @@ function isValStudioPath(pathname) {
447
513
  return pathname.startsWith("/val");
448
514
  }
449
515
 
516
+ // Same guards as the mountOverlay effect: suspending where the overlay can't
517
+ // mount would only ever stall into the waitForLoad timeout. Browser-only.
518
+ function shouldEnableVal() {
519
+ if (location.search === "?message_onready=true") {
520
+ return false;
521
+ }
522
+ if (isValStudioPath(location.pathname)) {
523
+ return false;
524
+ }
525
+ return hasValEnableCookie(document.cookie);
526
+ }
527
+
450
528
  // function ValIcon() {
451
529
  // return (
452
530
  // <svg
@@ -1,17 +1,19 @@
1
1
  'use client';
2
2
  import { _ as _slicedToArray } from './slicedToArray-aa291011.esm.js';
3
- import { _ as _objectSpread2 } from './objectSpread2-c1340c1c.esm.js';
4
- import { DEFAULT_CONTENT_HOST, Internal } from '@valbuild/core';
3
+ import { _ as _objectSpread2 } from './objectSpread2-60d1bd93.esm.js';
4
+ import { Internal, DEFAULT_CONTENT_HOST } from '@valbuild/core';
5
5
  import { VERSION, VAL_APP_PATH, VAL_OVERLAY_ID } from '@valbuild/ui';
6
6
  import { useRouter, usePathname } from 'next/navigation';
7
7
  import Script from 'next/script';
8
8
  import React, { useEffect } from 'react';
9
- import { ValExternalStore, ValOverlayProvider } from './ValOverlayContext-6635a4d7.esm.js';
9
+ import { ValExternalStore, ValOverlayProvider } from './ValOverlayContext-3c37e5a7.esm.js';
10
10
  import { SET_AUTO_TAG_JSX_ENABLED } from '@valbuild/react/stega';
11
11
  import { VAL_THEME_SESSION_STORAGE_KEY, createValClient } from '@valbuild/shared/internal';
12
12
  import { p as prefixStyles, u as useConfigStorageSave, v as valPrefixedClass, c as cn } from './cssUtils-b5651c03.esm.js';
13
+ import { _ as _createForOfIteratorHelper } from './createForOfIteratorHelper-5758a730.esm.js';
13
14
  import { jsxs, jsx } from 'react/jsx-runtime';
14
15
  import './unsupportedIterableToArray-5baabfdc.esm.js';
16
+ import './defineProperty-cca5affa.esm.js';
15
17
 
16
18
  function initSessionTheme(config) {
17
19
  if (typeof window === "undefined") {
@@ -32,6 +34,39 @@ function initSessionTheme(config) {
32
34
  return theme;
33
35
  }
34
36
 
37
+ /**
38
+ * Returns true if the Val Enable cookie is set to "true" in the given cookie
39
+ * string (typically `document.cookie`).
40
+ *
41
+ * Exact-token match: a naive `includes("val_enable=true")` would also match
42
+ * unrelated cookies like `xval_enable=true`. The server sets the cookie to
43
+ * "false" (rather than deleting it) on disable, so the value must be checked
44
+ * too.
45
+ */
46
+ function hasValEnableCookie(cookieString) {
47
+ var _iterator = _createForOfIteratorHelper(cookieString.split(";")),
48
+ _step;
49
+ try {
50
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
51
+ var part = _step.value;
52
+ var eq = part.indexOf("=");
53
+ if (eq === -1) {
54
+ continue;
55
+ }
56
+ var name = part.slice(0, eq).trim();
57
+ var value = part.slice(eq + 1).trim();
58
+ if (name === Internal.VAL_ENABLE_COOKIE_NAME && value === "true") {
59
+ return true;
60
+ }
61
+ }
62
+ } catch (err) {
63
+ _iterator.e(err);
64
+ } finally {
65
+ _iterator.f();
66
+ }
67
+ return false;
68
+ }
69
+
35
70
  var ValNextProvider = function ValNextProvider(props) {
36
71
  // TODO: use config:
37
72
  var route = "/api/val";
@@ -45,27 +80,40 @@ var ValNextProvider = function ValNextProvider(props) {
45
80
  var valStore = React.useMemo(function () {
46
81
  return new ValExternalStore();
47
82
  }, []);
48
- var _React$useState = React.useState(),
83
+ // Whether useValStega should actually suspend. False during SSR and the
84
+ // hydration render — the server store is never populated (draft data
85
+ // arrives via browser CustomEvents only), so suspending there would just
86
+ // stall into the waitForLoad timeout, and hydration must render the static
87
+ // source so it matches the server HTML exactly. Activated post-hydration
88
+ // (in an effect, only when the Val Enable cookie is present) inside a
89
+ // transition: React keeps the static content visible while hooks suspend
90
+ // and then swaps to draft data as a normal update — no Suspense fallback
91
+ // flash and no hydration mismatch.
92
+ var _React$useState = React.useState(false),
49
93
  _React$useState2 = _slicedToArray(_React$useState, 2),
50
- mountOverlay = _React$useState2[0],
51
- setMountOverlay = _React$useState2[1];
52
- var _React$useState3 = React.useState(null),
94
+ suspendActive = _React$useState2[0],
95
+ setSuspendActive = _React$useState2[1];
96
+ var _React$useState3 = React.useState(),
53
97
  _React$useState4 = _slicedToArray(_React$useState3, 2),
54
- draftMode = _React$useState4[0],
55
- setDraftMode = _React$useState4[1];
56
- var _React$useState5 = React.useState(false),
98
+ mountOverlay = _React$useState4[0],
99
+ setMountOverlay = _React$useState4[1];
100
+ var _React$useState5 = React.useState(null),
57
101
  _React$useState6 = _slicedToArray(_React$useState5, 2),
58
- spaReady = _React$useState6[0],
59
- setSpaReady = _React$useState6[1]; // TODO: consider removing spaReady - it is not used? If we remove, clean up the custom events that send the message too...
102
+ draftMode = _React$useState6[0],
103
+ setDraftMode = _React$useState6[1];
104
+ var _React$useState7 = React.useState(false),
105
+ _React$useState8 = _slicedToArray(_React$useState7, 2),
106
+ spaReady = _React$useState8[0],
107
+ setSpaReady = _React$useState8[1]; // TODO: consider removing spaReady - it is not used? If we remove, clean up the custom events that send the message too...
60
108
  var router = useRouter();
61
109
  var _React$useTransition = React.useTransition(),
62
110
  _React$useTransition2 = _slicedToArray(_React$useTransition, 2),
63
111
  startTransition = _React$useTransition2[1];
64
112
  var rerenderCounterRef = React.useRef(0);
65
- var _React$useState7 = React.useState(null),
66
- _React$useState8 = _slicedToArray(_React$useState7, 2),
67
- iframeSrc = _React$useState8[0],
68
- setIframeSrc = _React$useState8[1];
113
+ var _React$useState9 = React.useState(null),
114
+ _React$useState0 = _slicedToArray(_React$useState9, 2),
115
+ iframeSrc = _React$useState0[0],
116
+ setIframeSrc = _React$useState0[1];
69
117
  var pathname = usePathname();
70
118
  useEffect(function () {
71
119
  window.dispatchEvent(new CustomEvent("val-provider:pathname", {
@@ -82,8 +130,21 @@ var ValNextProvider = function ValNextProvider(props) {
82
130
  setMountOverlay(false);
83
131
  return;
84
132
  }
85
- setMountOverlay(document.cookie.includes("".concat(Internal.VAL_ENABLE_COOKIE_NAME, "=true")));
133
+ setMountOverlay(hasValEnableCookie(document.cookie));
86
134
  }, []);
135
+ React.useEffect(function () {
136
+ // Activate the Suspense gate after hydration. Inside a transition so
137
+ // already-visible (static) content stays on screen while useValStega
138
+ // suspends — no fallback flash — and the swap to draft data commits as a
139
+ // normal update instead of a hydration mismatch. Never deactivated:
140
+ // components must not stop suspending across renders (the draft-mode-off
141
+ // release valve lives in useValStega instead).
142
+ if (props.suspend && shouldEnableVal()) {
143
+ startTransition(function () {
144
+ setSuspendActive(true);
145
+ });
146
+ }
147
+ }, [props.suspend]);
87
148
  React.useEffect(function () {
88
149
  if (!mountOverlay) {
89
150
  return;
@@ -170,7 +231,11 @@ var ValNextProvider = function ValNextProvider(props) {
170
231
  return;
171
232
  }
172
233
  if (res.status === 401) {
173
- // ignore when not authorized
234
+ // Not authorized (e.g. stale Val Enable cookie after the session
235
+ // expired): treat draft mode as off so useValStega's Suspense gate
236
+ // is released instead of leaving draftMode stuck at null and
237
+ // re-suspending into the waitForLoad timeout.
238
+ setDraftMode(false);
174
239
  return;
175
240
  }
176
241
  if (res.status !== 200) {
@@ -266,10 +331,10 @@ var ValNextProvider = function ValNextProvider(props) {
266
331
  window.dispatchEvent(new CustomEvent("val-append-overlay"));
267
332
  }
268
333
  });
269
- var _React$useState9 = React.useState(null),
270
- _React$useState0 = _slicedToArray(_React$useState9, 2),
271
- dropZone = _React$useState0[0],
272
- setDropZone = _React$useState0[1];
334
+ var _React$useState1 = React.useState(null),
335
+ _React$useState10 = _slicedToArray(_React$useState1, 2),
336
+ dropZone = _React$useState10[0],
337
+ setDropZone = _React$useState10[1];
273
338
  React.useEffect(function () {
274
339
  var storedDropZone = localStorage.getItem("val-menu-drop-zone-default");
275
340
  if (storedDropZone) {
@@ -282,10 +347,10 @@ var ValNextProvider = function ValNextProvider(props) {
282
347
  var initTheme = React.useMemo(function () {
283
348
  return initSessionTheme(props.config);
284
349
  }, [props.config]);
285
- var _React$useState1 = React.useState(false),
286
- _React$useState10 = _slicedToArray(_React$useState1, 2),
287
- spaLoaded = _React$useState10[0],
288
- setSpaLoaded = _React$useState10[1];
350
+ var _React$useState11 = React.useState(false),
351
+ _React$useState12 = _slicedToArray(_React$useState11, 2),
352
+ spaLoaded = _React$useState12[0],
353
+ setSpaLoaded = _React$useState12[1];
289
354
  React.useEffect(function () {
290
355
  var listener = function listener() {
291
356
  setSpaLoaded(true);
@@ -325,6 +390,7 @@ var ValNextProvider = function ValNextProvider(props) {
325
390
  }, [valPrefixedClass]);
326
391
  return /*#__PURE__*/jsxs(ValOverlayProvider, {
327
392
  draftMode: draftMode,
393
+ suspend: suspendActive,
328
394
  store: valStore,
329
395
  children: [props.children, dropZone !== null && !spaLoaded && mountOverlay && initTheme !== null && /*#__PURE__*/jsxs(React.Fragment, {
330
396
  children: [/*#__PURE__*/jsx("style", {
@@ -438,6 +504,18 @@ function isValStudioPath(pathname) {
438
504
  return pathname.startsWith("/val");
439
505
  }
440
506
 
507
+ // Same guards as the mountOverlay effect: suspending where the overlay can't
508
+ // mount would only ever stall into the waitForLoad timeout. Browser-only.
509
+ function shouldEnableVal() {
510
+ if (location.search === "?message_onready=true") {
511
+ return false;
512
+ }
513
+ if (isValStudioPath(location.pathname)) {
514
+ return false;
515
+ }
516
+ return hasValEnableCookie(document.cookie);
517
+ }
518
+
441
519
  // function ValIcon() {
442
520
  // return (
443
521
  // <svg