@valbuild/next 0.96.3 → 0.97.0

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 (44) 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/{ValNextProvider-26ef0c1c.cjs.dev.js → ValNextProvider-03a9f8fd.cjs.dev.js} +103 -25
  6. package/dist/ValNextProvider-6103cb74.cjs.js +7 -0
  7. package/dist/{ValNextProvider-b0d9fa24.cjs.prod.js → ValNextProvider-6103cb74.cjs.prod.js} +103 -25
  8. package/dist/{ValNextProvider-e163e127.esm.js → ValNextProvider-7302b8af.esm.js} +104 -26
  9. package/dist/ValOverlayContext-3c37e5a7.esm.js +251 -0
  10. package/dist/ValOverlayContext-c6f27a6d.cjs.dev.js +262 -0
  11. package/dist/ValOverlayContext-f7f45bc7.cjs.js +7 -0
  12. package/dist/ValOverlayContext-f7f45bc7.cjs.prod.js +262 -0
  13. package/dist/declarations/src/ValProvider.d.ts +1 -0
  14. package/dist/declarations/src/initVal.d.ts +25 -0
  15. package/dist/defineProperty-8951f469.cjs.prod.js +29 -0
  16. package/dist/defineProperty-cca5affa.esm.js +26 -0
  17. package/dist/defineProperty-f90345d7.cjs.dev.js +29 -0
  18. package/dist/{objectSpread2-3c87fb4f.cjs.prod.js → objectSpread2-13f847a9.cjs.prod.js} +2 -27
  19. package/dist/{objectSpread2-792eb2c2.cjs.dev.js → objectSpread2-58024783.cjs.dev.js} +2 -27
  20. package/dist/{objectSpread2-c1340c1c.esm.js → objectSpread2-60d1bd93.esm.js} +2 -25
  21. package/dist/{routeFromVal-ef8c304a.cjs.prod.js → routeFromVal-13c832b2.cjs.prod.js} +1 -1
  22. package/dist/{routeFromVal-748d2aec.esm.js → routeFromVal-8855a5cc.esm.js} +1 -1
  23. package/dist/{routeFromVal-8fdaa0d0.cjs.dev.js → routeFromVal-9b610e77.cjs.dev.js} +1 -1
  24. package/dist/valbuild-next.cjs.dev.js +46 -3
  25. package/dist/valbuild-next.cjs.prod.js +46 -3
  26. package/dist/valbuild-next.esm.js +46 -3
  27. package/dist/{asyncToGenerator-8e5c36c8.cjs.prod.js → version-0e6da9a7.cjs.prod.js} +10 -0
  28. package/dist/{asyncToGenerator-500f022f.esm.js → version-4d7b692c.esm.js} +10 -1
  29. package/dist/{asyncToGenerator-c3823d62.cjs.dev.js → version-c311d818.cjs.dev.js} +10 -0
  30. package/package.json +14 -8
  31. package/rsc/dist/valbuild-next-rsc.cjs.dev.js +35 -35
  32. package/rsc/dist/valbuild-next-rsc.cjs.prod.js +35 -35
  33. package/rsc/dist/valbuild-next-rsc.esm.js +4 -4
  34. package/server/dist/valbuild-next-server.cjs.dev.js +9 -9
  35. package/server/dist/valbuild-next-server.cjs.prod.js +9 -9
  36. package/server/dist/valbuild-next-server.esm.js +3 -3
  37. package/dist/ValNextProvider-b0d9fa24.cjs.js +0 -7
  38. package/dist/ValOverlayContext-6635a4d7.esm.js +0 -114
  39. package/dist/ValOverlayContext-942f1294.cjs.js +0 -7
  40. package/dist/ValOverlayContext-942f1294.cjs.prod.js +0 -125
  41. package/dist/ValOverlayContext-c82a4507.cjs.dev.js +0 -125
  42. package/dist/version-16f6b0ce.esm.js +0 -10
  43. package/dist/version-d034b2fc.cjs.dev.js +0 -12
  44. 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-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
@@ -0,0 +1,251 @@
1
+ 'use client';
2
+ import { _ as _createForOfIteratorHelper } from './createForOfIteratorHelper-5758a730.esm.js';
3
+ import { a as _toPropertyKey, _ as _defineProperty } from './defineProperty-cca5affa.esm.js';
4
+ import React from 'react';
5
+ import { jsx } from 'react/jsx-runtime';
6
+ import './unsupportedIterableToArray-5baabfdc.esm.js';
7
+
8
+ function _classCallCheck(a, n) {
9
+ if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function");
10
+ }
11
+
12
+ function _defineProperties(e, r) {
13
+ for (var t = 0; t < r.length; t++) {
14
+ var o = r[t];
15
+ o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o);
16
+ }
17
+ }
18
+ function _createClass(e, r, t) {
19
+ return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", {
20
+ writable: !1
21
+ }), e;
22
+ }
23
+
24
+ var LOAD_TIMEOUT_MS = 10000;
25
+ var ValExternalStore = /*#__PURE__*/function () {
26
+ // Path-keyed cache of every source seen via update(). The single source of
27
+ // truth: load state and snapshots are both derived from this, so data can be
28
+ // queried for any combination of paths even before a subscriber registers.
29
+
30
+ // Reference-stable per-subscriberId snapshot cache for useSyncExternalStore.
31
+ // Built lazily from loadedSources in get() and invalidated in update().
32
+
33
+ // One-shot listeners used solely by waitForLoad. Kept separate from the
34
+ // useSyncExternalStore listeners so waitForLoad never has to register (and
35
+ // leak) a subscriberId.
36
+
37
+ function ValExternalStore() {
38
+ var _this = this;
39
+ _classCallCheck(this, ValExternalStore);
40
+ _defineProperty(this, "subscribe", function (paths) {
41
+ return function (listener) {
42
+ var subscriberId = createSubscriberId(paths);
43
+ var existing = _this.listeners.get(subscriberId);
44
+ if (existing) {
45
+ existing.push(listener);
46
+ } else {
47
+ _this.listeners.set(subscriberId, [listener]);
48
+ }
49
+ return function () {
50
+ var current = _this.listeners.get(subscriberId);
51
+ if (!current) return;
52
+ var idx = current.indexOf(listener);
53
+ if (idx >= 0) current.splice(idx, 1);
54
+ if (current.length === 0) _this.listeners["delete"](subscriberId);
55
+ };
56
+ };
57
+ });
58
+ _defineProperty(this, "getSnapshot", function (paths) {
59
+ return function () {
60
+ return _this.get(paths);
61
+ };
62
+ });
63
+ _defineProperty(this, "getServerSnapshot", function (paths) {
64
+ return function () {
65
+ return _this.get(paths);
66
+ };
67
+ });
68
+ _defineProperty(this, "get", function (paths) {
69
+ var subscriberId = createSubscriberId(paths);
70
+ if (_this.snapshots.has(subscriberId)) {
71
+ return _this.snapshots.get(subscriberId);
72
+ }
73
+ // Build the snapshot from the path-keyed loadedSources so data that arrived
74
+ // before this subscriber registered is still returned. Cache it for a
75
+ // reference-stable result (required by useSyncExternalStore).
76
+ var record;
77
+ var _iterator = _createForOfIteratorHelper(paths),
78
+ _step;
79
+ try {
80
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
81
+ var p = _step.value;
82
+ // Json never holds `undefined`, so a defined value means the path loaded.
83
+ var loaded = _this.loadedSources.get(p);
84
+ if (loaded !== undefined) {
85
+ if (!record) {
86
+ record = {};
87
+ }
88
+ record[p] = loaded;
89
+ }
90
+ }
91
+ } catch (err) {
92
+ _iterator.e(err);
93
+ } finally {
94
+ _iterator.f();
95
+ }
96
+ _this.snapshots.set(subscriberId, record);
97
+ return record;
98
+ });
99
+ _defineProperty(this, "hasAllLoaded", function (paths) {
100
+ var _iterator2 = _createForOfIteratorHelper(paths),
101
+ _step2;
102
+ try {
103
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
104
+ var p = _step2.value;
105
+ if (!_this.loadedSources.has(p)) {
106
+ return false;
107
+ }
108
+ }
109
+ } catch (err) {
110
+ _iterator2.e(err);
111
+ } finally {
112
+ _iterator2.f();
113
+ }
114
+ return true;
115
+ });
116
+ /**
117
+ * Returns a cached promise that resolves once `update()` has populated
118
+ * every path in `paths`. The same promise instance is returned for the
119
+ * same paths until it resolves — required so `React.use` / classic
120
+ * Suspense doesn't re-suspend on every render.
121
+ *
122
+ * If the data never arrives (network failure, a path that is never
123
+ * `update()`'d, a typo in a module path) the promise would otherwise hang
124
+ * forever and keep the Suspense boundary in its fallback. To avoid a silent
125
+ * hang it resolves anyway after `LOAD_TIMEOUT_MS`, logging an error so the
126
+ * failure is diagnosable while the page still renders with partial data.
127
+ */
128
+ _defineProperty(this, "waitForLoad", function (paths) {
129
+ if (_this.hasAllLoaded(paths)) {
130
+ return Promise.resolve();
131
+ }
132
+ var subscriberId = createSubscriberId(paths);
133
+ var existing = _this.loadPromises.get(subscriberId);
134
+ if (existing) {
135
+ return existing;
136
+ }
137
+ var promise = new Promise(function (resolve) {
138
+ var cleanup = function cleanup() {
139
+ clearTimeout(timeout);
140
+ _this.loadListeners["delete"](listener);
141
+ _this.loadPromises["delete"](subscriberId);
142
+ };
143
+ var listener = function listener() {
144
+ if (_this.hasAllLoaded(paths)) {
145
+ cleanup();
146
+ resolve();
147
+ }
148
+ };
149
+ var timeout = setTimeout(function () {
150
+ var missing = paths.filter(function (p) {
151
+ return !_this.loadedSources.has(p);
152
+ });
153
+ console.error("Val: draft module(s) did not load within ".concat(LOAD_TIMEOUT_MS, "ms; rendering with partial data. Missing: ").concat(missing.join(", ")));
154
+ cleanup();
155
+ resolve();
156
+ }, LOAD_TIMEOUT_MS);
157
+ // Don't let the pending timer keep a Node process (e.g. the test runner
158
+ // or SSR) alive; setTimeout returns a number in the browser, where there
159
+ // is nothing to unref.
160
+ if (typeof timeout !== "number") {
161
+ timeout.unref();
162
+ }
163
+ _this.loadListeners.add(listener);
164
+ });
165
+ _this.loadPromises.set(subscriberId, promise);
166
+ return promise;
167
+ });
168
+ this.listeners = new Map();
169
+ this.loadPromises = new Map();
170
+ this.loadedSources = new Map();
171
+ this.snapshots = new Map();
172
+ this.loadListeners = new Set();
173
+ }
174
+ return _createClass(ValExternalStore, [{
175
+ key: "update",
176
+ value: function update(path, source) {
177
+ this.loadedSources.set(path, source);
178
+ // Invalidate cached snapshots that include this path so the next get()
179
+ // rebuilds a fresh (new-reference) record, then notify their listeners.
180
+ for (var _i = 0, _Array$from = Array.from(this.snapshots.keys()); _i < _Array$from.length; _i++) {
181
+ var subscriberId = _Array$from[_i];
182
+ if (subscriberId.includes(path)) {
183
+ // TODO: hash paths instead
184
+ this.snapshots["delete"](subscriberId);
185
+ }
186
+ }
187
+ var _iterator3 = _createForOfIteratorHelper(this.listeners.keys()),
188
+ _step3;
189
+ try {
190
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
191
+ var _subscriberId = _step3.value;
192
+ if (_subscriberId.includes(path)) {
193
+ this.emitChange(_subscriberId);
194
+ }
195
+ }
196
+ } catch (err) {
197
+ _iterator3.e(err);
198
+ } finally {
199
+ _iterator3.f();
200
+ }
201
+ for (var _i2 = 0, _Array$from2 = Array.from(this.loadListeners); _i2 < _Array$from2.length; _i2++) {
202
+ var listener = _Array$from2[_i2];
203
+ listener();
204
+ }
205
+ }
206
+ }, {
207
+ key: "emitChange",
208
+ value: function emitChange(subscriberId) {
209
+ var _this$listeners$get;
210
+ var _iterator4 = _createForOfIteratorHelper((_this$listeners$get = this.listeners.get(subscriberId)) !== null && _this$listeners$get !== void 0 ? _this$listeners$get : []),
211
+ _step4;
212
+ try {
213
+ for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) {
214
+ var listener = _step4.value;
215
+ listener();
216
+ }
217
+ } catch (err) {
218
+ _iterator4.e(err);
219
+ } finally {
220
+ _iterator4.f();
221
+ }
222
+ }
223
+ }]);
224
+ }();
225
+ function createSubscriberId(paths) {
226
+ return paths.slice().sort().join("&");
227
+ }
228
+ var ValOverlayContext = /*#__PURE__*/React.createContext({
229
+ store: undefined,
230
+ draftMode: false,
231
+ suspend: false
232
+ });
233
+ function ValOverlayProvider(_ref) {
234
+ var store = _ref.store,
235
+ draftMode = _ref.draftMode,
236
+ suspend = _ref.suspend,
237
+ children = _ref.children;
238
+ return /*#__PURE__*/jsx(ValOverlayContext.Provider, {
239
+ value: {
240
+ store: store,
241
+ draftMode: draftMode,
242
+ suspend: suspend
243
+ },
244
+ children: children
245
+ });
246
+ }
247
+ var useValOverlayContext = function useValOverlayContext() {
248
+ return React.useContext(ValOverlayContext);
249
+ };
250
+
251
+ export { ValExternalStore, ValOverlayContext, ValOverlayProvider, useValOverlayContext };