@valbuild/tanstack 0.139.1 → 0.140.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 (29) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +51 -4
  3. package/client/dist/valbuild-tanstack-client.cjs.dev.js +53 -6
  4. package/client/dist/valbuild-tanstack-client.cjs.prod.js +53 -6
  5. package/client/dist/valbuild-tanstack-client.esm.js +53 -6
  6. package/dist/{ValOverlayContext-5f1d8da4.cjs.prod.js → ValOverlayContext-2739cc60.cjs.prod.js} +4 -2
  7. package/dist/{ValOverlayContext-5bc3830f.esm.js → ValOverlayContext-28101df3.esm.js} +4 -2
  8. package/dist/{ValOverlayContext-3ec15518.cjs.dev.js → ValOverlayContext-bafe3de9.cjs.dev.js} +4 -2
  9. package/dist/{createForOfIteratorHelper-e75681d7.cjs.dev.js → createForOfIteratorHelper-4497beef.cjs.dev.js} +2 -0
  10. package/dist/{createForOfIteratorHelper-485c2ce1.esm.js → createForOfIteratorHelper-93a62170.esm.js} +1 -1
  11. package/dist/{createForOfIteratorHelper-0324e063.cjs.prod.js → createForOfIteratorHelper-9afd7d2d.cjs.prod.js} +2 -0
  12. package/dist/declarations/src/ValDraft.d.ts +21 -0
  13. package/dist/declarations/src/ValProvider.d.ts +1 -0
  14. package/dist/declarations/src/external_exempt_from_val_quickjs.d.ts +1 -0
  15. package/dist/declarations/src/server/index.d.ts +1 -0
  16. package/dist/declarations/src/server/initValContent.d.ts +30 -0
  17. package/dist/{routeFromVal-6693e037.cjs.prod.js → routeFromVal-0b39e8e3.cjs.prod.js} +1 -1
  18. package/dist/{routeFromVal-728295a0.esm.js → routeFromVal-1221e70d.esm.js} +1 -1
  19. package/dist/{routeFromVal-a9147ceb.cjs.dev.js → routeFromVal-97111bfd.cjs.dev.js} +1 -1
  20. package/dist/valbuild-tanstack.cjs.dev.js +50 -12
  21. package/dist/valbuild-tanstack.cjs.prod.js +50 -12
  22. package/dist/valbuild-tanstack.esm.js +50 -12
  23. package/dist/{version-7d6c8558.cjs.dev.js → version-0cfddc38.cjs.dev.js} +1 -1
  24. package/dist/{version-0911ff0a.cjs.prod.js → version-24675097.cjs.prod.js} +1 -1
  25. package/dist/{version-121c6f89.esm.js → version-e61be600.esm.js} +1 -1
  26. package/package.json +7 -7
  27. package/server/dist/valbuild-tanstack-server.cjs.dev.js +478 -152
  28. package/server/dist/valbuild-tanstack-server.cjs.prod.js +478 -152
  29. package/server/dist/valbuild-tanstack-server.esm.js +479 -153
package/CHANGELOG.md CHANGED
@@ -1,5 +1,64 @@
1
1
  # @valbuild/tanstack
2
2
 
3
+ ## 0.140.0
4
+
5
+ ### Patch Changes
6
+
7
+ - [#793](https://github.com/valbuild/val/pull/793) [`092e6a7`](https://github.com/valbuild/val/commit/092e6a7f78bcfcf4c6cecaf34407fdfc580ebbc1) Thanks [@freekh](https://github.com/freekh)! - A draft page on TanStack Start is now rendered as the draft by the server, so it no longer shows the published text first and the draft a moment later — most noticeable when you reload right after publishing.
8
+
9
+ Read the request's draft in your site layout's loader and pass it to `ValProvider`:
10
+
11
+ ```tsx
12
+ // src/val/server.ts
13
+ export const { fetchValDraft /* , fetchVal, ... */ } = initValContent(
14
+ config,
15
+ valModules,
16
+ { draftMode },
17
+ );
18
+
19
+ // src/routes/_site.tsx
20
+ const getValDraft = createServerFn().handler(() => fetchValDraft());
21
+
22
+ export const Route = createFileRoute("/_site")({
23
+ loader: () => (typeof document === "undefined" ? getValDraft() : null),
24
+ component: SiteLayout,
25
+ });
26
+
27
+ function SiteLayout() {
28
+ const draft = Route.useLoaderData();
29
+ return (
30
+ <ValProvider config={config} suspend draft={draft}>
31
+ {/* ... */}
32
+ </ValProvider>
33
+ );
34
+ }
35
+ ```
36
+
37
+ Visitors pay one cookie lookup and nothing else. Without `draft`, pages behave as before. Edited `.jsonValues()` entries are rendered as the draft too; the others render from the build, as they do for visitors.
38
+
39
+ Also: a draft's `.jsonValues()` entries now include changes that were published after the build being served, instead of showing the old value until the next build is live.
40
+
41
+ Also: a renamed or duplicated `.jsonValues()` entry now has its content in the draft, on the page and when the Studio reads the entry, instead of failing to load until it is published.
42
+
43
+ - Updated dependencies [[`cb93874`](https://github.com/valbuild/val/commit/cb938748aec15fd0ac3814c1696a2dd78ad63c27), [`092e6a7`](https://github.com/valbuild/val/commit/092e6a7f78bcfcf4c6cecaf34407fdfc580ebbc1)]:
44
+ - @valbuild/ui@0.140.0
45
+ - @valbuild/server@0.140.0
46
+ - @valbuild/shared@0.140.0
47
+ - @valbuild/react@0.140.0
48
+ - @valbuild/language-server@0.140.0
49
+ - @valbuild/mcp@0.140.0
50
+
51
+ ## 0.139.2
52
+
53
+ ### Patch Changes
54
+
55
+ - Updated dependencies [[`4d9e12c`](https://github.com/valbuild/val/commit/4d9e12c5286f981c7a8a7820db1bf580e327d2ea), [`1c3eb3a`](https://github.com/valbuild/val/commit/1c3eb3af91f6dfeff94a95d80d5291a1acf1baf2)]:
56
+ - @valbuild/ui@0.139.2
57
+ - @valbuild/react@0.139.2
58
+ - @valbuild/server@0.139.2
59
+ - @valbuild/language-server@0.139.2
60
+ - @valbuild/mcp@0.139.2
61
+
3
62
  ## 0.139.1
4
63
 
5
64
  ### Patch Changes
package/README.md CHANGED
@@ -209,6 +209,9 @@ export const Route = createFileRoute("/_site")({
209
209
  });
210
210
  ```
211
211
 
212
+ Add the `draft` prop from [Preview and draft mode](#preview-and-draft-mode) so a
213
+ draft page is rendered as the draft on the server too.
214
+
212
215
  A pathless layout adds no URL segment: `_site.index.tsx` is still `/`. Val
213
216
  modules named after those files follow the same rule, so
214
217
  `_site.posts.$postId.val.ts` holds `/posts/...` keys.
@@ -234,9 +237,10 @@ export const {
234
237
  ## Reading content
235
238
 
236
239
  **Prefer the hooks.** They work in both places a component runs: during SSR they
237
- resolve the published content, and in a browser with the Studio open they
238
- resolve what the editor currently holds — so an edit appears as it is typed,
239
- with no round trip and no loader.
240
+ resolve the published content — or, when `ValProvider` is given the request's
241
+ draft (see [Preview and draft mode](#preview-and-draft-mode)), the draft — and in
242
+ a browser with the Studio open they resolve what the editor currently holds, so
243
+ an edit appears as it is typed, with no round trip and no loader.
240
244
 
241
245
  ```tsx
242
246
  function Page() {
@@ -330,10 +334,52 @@ its own, so this package brings a cookie (`val_draft_mode`, `valDraftMode()` in
330
334
  read also carries Val's session cookie, which is a signed JWT the server
331
335
  verifies, so a forged draft cookie gets published content.
332
336
 
337
+ **Render the draft on the server, too.** Without it the server renders the
338
+ PUBLISHED page and the browser swaps the draft in once the overlay has loaded,
339
+ so every draft page load shows the old text first and the new text a moment
340
+ later. Read the request's draft in the site layout's loader and hand it to the
341
+ provider:
342
+
343
+ ```tsx
344
+ // src/val/server.ts: add `fetchValDraft` to what initValContent returns
345
+ export const { fetchValDraft /* , fetchVal, ... */ } = initValContent(
346
+ config,
347
+ valModules,
348
+ { draftMode },
349
+ );
350
+
351
+ // src/routes/_site.tsx
352
+ const getValDraft = createServerFn().handler(() => fetchValDraft());
353
+
354
+ export const Route = createFileRoute("/_site")({
355
+ // On the server only: the overlay keeps the page up to date after that.
356
+ loader: () => (typeof document === "undefined" ? getValDraft() : null),
357
+ component: SiteLayout,
358
+ });
359
+
360
+ function SiteLayout() {
361
+ const draft = Route.useLoaderData();
362
+ return (
363
+ <ValProvider config={config} suspend draft={draft}>
364
+ {/* ... */}
365
+ </ValProvider>
366
+ );
367
+ }
368
+ ```
369
+
370
+ `fetchValDraft` returns `null` — after one cookie lookup, with no read — unless
371
+ the request is in draft mode, so visitors pay nothing. In draft mode it returns
372
+ the modules the draft changes, which the server renders with and the browser
373
+ hydrates from: both produce the same page, showing the draft from the first
374
+ paint. It becomes click-to-editable as each component finishes hydrating, and
375
+ the overlay takes over from there.
376
+
333
377
  `suspend` on `ValProvider` makes `useValStega` / `useValRouteStega` wait for
334
378
  draft data before rendering, so a page that exists **only** in an unpublished
335
379
  draft renders instead of 404ing. Visitors without the Val Enable cookie pay
336
- nothing for it. It needs React 19 and a `<Suspense>` boundary (see above).
380
+ nothing for it. It needs React 19 and a `<Suspense>` boundary (see above). With
381
+ `draft` given, the server already has that page's content, so its first render
382
+ does not 404 either.
337
383
 
338
384
  ## Images
339
385
 
@@ -370,6 +416,7 @@ SDK of your choice. See [`@valbuild/mcp`](https://www.npmjs.com/package/@valbuil
370
416
  | `valNextAppRouter` | `valApiHandler` — a `Request` in, a `Response` out |
371
417
  | `ValImage` wraps `next/image` | `ValImage` is an `<img>` |
372
418
  | `fetchVal` in a Server Component | hooks in components; `createServerFn` for loaders |
419
+ | client hooks render published SSR | `ValProvider draft` renders the draft during SSR |
373
420
 
374
421
  ## Schema reference
375
422
 
@@ -2,12 +2,12 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var routeFromVal = require('../../dist/routeFromVal-a9147ceb.cjs.dev.js');
5
+ var routeFromVal = require('../../dist/routeFromVal-97111bfd.cjs.dev.js');
6
6
  var core = require('@valbuild/core');
7
7
  var stega = require('@valbuild/react/stega');
8
8
  var React = require('react');
9
- var ValOverlayContext = require('../../dist/ValOverlayContext-3ec15518.cjs.dev.js');
10
- require('../../dist/createForOfIteratorHelper-e75681d7.cjs.dev.js');
9
+ var ValOverlayContext = require('../../dist/ValOverlayContext-bafe3de9.cjs.dev.js');
10
+ require('../../dist/createForOfIteratorHelper-4497beef.cjs.dev.js');
11
11
  require('@valbuild/shared/client');
12
12
  require('@valbuild/core/patch');
13
13
  require('react/jsx-runtime');
@@ -16,10 +16,55 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
16
16
 
17
17
  var React__default = /*#__PURE__*/_interopDefault(React);
18
18
 
19
+ var subscribeNever = function subscribeNever() {
20
+ return function () {};
21
+ };
22
+
23
+ /**
24
+ * `false` for the server's render and for this component's hydration render,
25
+ * `true` for every render after.
26
+ *
27
+ * Per component, which is the point: React reads `getServerSnapshot` while it
28
+ * hydrates a component and `getSnapshot` otherwise, and re-renders the
29
+ * component right after hydrating it when the two differ. So a component that
30
+ * hydrates late -- a route split out into its own chunk -- still renders its
31
+ * hydration pass as the server did, however long after the rest of the page.
32
+ */
33
+ function useHydrated() {
34
+ return React__default["default"].useSyncExternalStore(subscribeNever, function () {
35
+ return true;
36
+ }, function () {
37
+ return false;
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Whether the content this component renders carries its edit tags.
43
+ *
44
+ * Always in draft mode -- except, on a page rendered from a server-read draft,
45
+ * for the server's render and for this component's own hydration render: then
46
+ * the draft text goes out without them, and the render React does right after
47
+ * hydrating the component puts them in.
48
+ *
49
+ * Because JSX auto-tagging turns a tagged string into a `data-val-path`
50
+ * attribute as the element is created, and the switch for it is process-wide.
51
+ * The server cannot be relied on to tag (whether the patched JSX runtime is
52
+ * the one it renders with depends on how it was bundled), and no page-wide
53
+ * moment means "everything has hydrated": a split-out route, or a lazy child
54
+ * handed a string as a prop, hydrates after the rest of the page. A string
55
+ * with no tags in it has nothing to tag, wherever it is rendered and whatever
56
+ * the switch says -- so the server's HTML and every hydration render agree.
57
+ */
58
+ function useEncodesEditTags(context) {
59
+ var hydrated = useHydrated();
60
+ return !!context.draftMode && (!context.serverDraft || hydrated);
61
+ }
62
+
19
63
  /** What `useVal` gives back — see `ResolvedVal` in `@valbuild/react/stega`. */
20
64
 
21
65
  function useValStega(selector) {
22
66
  var valOverlayContext = ValOverlayContext.useValOverlayContext();
67
+ var encodes = useEncodesEditTags(valOverlayContext);
23
68
  var moduleIds = React__default["default"].useMemo(function () {
24
69
  return stega.getModuleIds(selector);
25
70
  }, [selector]);
@@ -72,7 +117,7 @@ function useValStega(selector) {
72
117
  React__default["default"].use(store.waitForLoad(moduleIds));
73
118
  }
74
119
  return stega.stegaEncode(selector, {
75
- disabled: !valOverlayContext.draftMode,
120
+ disabled: !encodes,
76
121
  getModule: function getModule(moduleId) {
77
122
  if (moduleMap && valOverlayContext.draftMode) {
78
123
  return moduleMap[moduleId];
@@ -159,6 +204,7 @@ var jsonEntryPromiseCache = new Map();
159
204
  */
160
205
  function useValKeyStega(selector, key) {
161
206
  var valOverlayContext = ValOverlayContext.useValOverlayContext();
207
+ var encodes = useEncodesEditTags(valOverlayContext);
162
208
  // A view is a pointer: everything below reads a path, a schema and a source
163
209
  // off this, and a pointer has none of them. See `resolveViewedModule`.
164
210
  var valModule = core.Internal.resolveViewedModule(selector);
@@ -175,7 +221,7 @@ function useValKeyStega(selector, key) {
175
221
  content = readCommittedJsonEntry(valModule, key);
176
222
  }
177
223
  return stega.stegaEncode(content, {
178
- disabled: !valOverlayContext.draftMode,
224
+ disabled: !encodes,
179
225
  root: routeFromVal.getJsonEntryStegaRoot(valModule, key)
180
226
  });
181
227
  }
@@ -234,6 +280,7 @@ function useValRouteStega(selector, params) {
234
280
  // router `val` is unused (we resolve a single entry below instead); for any
235
281
  // other router `draftSource` is.
236
282
  var val = useValStega(valModule);
283
+ var encodes = useEncodesEditTags(valOverlayContext);
237
284
  var draftSource = useDraftModuleSource(valModule && core.Internal.getValPath(valModule) || undefined);
238
285
  var resolvedParams = resolveParams(params);
239
286
  // Careful: null means there was an error - undefined means no params
@@ -263,7 +310,7 @@ function useValRouteStega(selector, params) {
263
310
  return null;
264
311
  }
265
312
  return stega.stegaEncode(content, {
266
- disabled: !valOverlayContext.draftMode,
313
+ disabled: !encodes,
267
314
  root: routeFromVal.getJsonEntryStegaRoot(valModule, url)
268
315
  });
269
316
  }
@@ -2,12 +2,12 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var routeFromVal = require('../../dist/routeFromVal-6693e037.cjs.prod.js');
5
+ var routeFromVal = require('../../dist/routeFromVal-0b39e8e3.cjs.prod.js');
6
6
  var core = require('@valbuild/core');
7
7
  var stega = require('@valbuild/react/stega');
8
8
  var React = require('react');
9
- var ValOverlayContext = require('../../dist/ValOverlayContext-5f1d8da4.cjs.prod.js');
10
- require('../../dist/createForOfIteratorHelper-0324e063.cjs.prod.js');
9
+ var ValOverlayContext = require('../../dist/ValOverlayContext-2739cc60.cjs.prod.js');
10
+ require('../../dist/createForOfIteratorHelper-9afd7d2d.cjs.prod.js');
11
11
  require('@valbuild/shared/client');
12
12
  require('@valbuild/core/patch');
13
13
  require('react/jsx-runtime');
@@ -16,10 +16,55 @@ function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e };
16
16
 
17
17
  var React__default = /*#__PURE__*/_interopDefault(React);
18
18
 
19
+ var subscribeNever = function subscribeNever() {
20
+ return function () {};
21
+ };
22
+
23
+ /**
24
+ * `false` for the server's render and for this component's hydration render,
25
+ * `true` for every render after.
26
+ *
27
+ * Per component, which is the point: React reads `getServerSnapshot` while it
28
+ * hydrates a component and `getSnapshot` otherwise, and re-renders the
29
+ * component right after hydrating it when the two differ. So a component that
30
+ * hydrates late -- a route split out into its own chunk -- still renders its
31
+ * hydration pass as the server did, however long after the rest of the page.
32
+ */
33
+ function useHydrated() {
34
+ return React__default["default"].useSyncExternalStore(subscribeNever, function () {
35
+ return true;
36
+ }, function () {
37
+ return false;
38
+ });
39
+ }
40
+
41
+ /**
42
+ * Whether the content this component renders carries its edit tags.
43
+ *
44
+ * Always in draft mode -- except, on a page rendered from a server-read draft,
45
+ * for the server's render and for this component's own hydration render: then
46
+ * the draft text goes out without them, and the render React does right after
47
+ * hydrating the component puts them in.
48
+ *
49
+ * Because JSX auto-tagging turns a tagged string into a `data-val-path`
50
+ * attribute as the element is created, and the switch for it is process-wide.
51
+ * The server cannot be relied on to tag (whether the patched JSX runtime is
52
+ * the one it renders with depends on how it was bundled), and no page-wide
53
+ * moment means "everything has hydrated": a split-out route, or a lazy child
54
+ * handed a string as a prop, hydrates after the rest of the page. A string
55
+ * with no tags in it has nothing to tag, wherever it is rendered and whatever
56
+ * the switch says -- so the server's HTML and every hydration render agree.
57
+ */
58
+ function useEncodesEditTags(context) {
59
+ var hydrated = useHydrated();
60
+ return !!context.draftMode && (!context.serverDraft || hydrated);
61
+ }
62
+
19
63
  /** What `useVal` gives back — see `ResolvedVal` in `@valbuild/react/stega`. */
20
64
 
21
65
  function useValStega(selector) {
22
66
  var valOverlayContext = ValOverlayContext.useValOverlayContext();
67
+ var encodes = useEncodesEditTags(valOverlayContext);
23
68
  var moduleIds = React__default["default"].useMemo(function () {
24
69
  return stega.getModuleIds(selector);
25
70
  }, [selector]);
@@ -72,7 +117,7 @@ function useValStega(selector) {
72
117
  React__default["default"].use(store.waitForLoad(moduleIds));
73
118
  }
74
119
  return stega.stegaEncode(selector, {
75
- disabled: !valOverlayContext.draftMode,
120
+ disabled: !encodes,
76
121
  getModule: function getModule(moduleId) {
77
122
  if (moduleMap && valOverlayContext.draftMode) {
78
123
  return moduleMap[moduleId];
@@ -159,6 +204,7 @@ var jsonEntryPromiseCache = new Map();
159
204
  */
160
205
  function useValKeyStega(selector, key) {
161
206
  var valOverlayContext = ValOverlayContext.useValOverlayContext();
207
+ var encodes = useEncodesEditTags(valOverlayContext);
162
208
  // A view is a pointer: everything below reads a path, a schema and a source
163
209
  // off this, and a pointer has none of them. See `resolveViewedModule`.
164
210
  var valModule = core.Internal.resolveViewedModule(selector);
@@ -175,7 +221,7 @@ function useValKeyStega(selector, key) {
175
221
  content = readCommittedJsonEntry(valModule, key);
176
222
  }
177
223
  return stega.stegaEncode(content, {
178
- disabled: !valOverlayContext.draftMode,
224
+ disabled: !encodes,
179
225
  root: routeFromVal.getJsonEntryStegaRoot(valModule, key)
180
226
  });
181
227
  }
@@ -234,6 +280,7 @@ function useValRouteStega(selector, params) {
234
280
  // router `val` is unused (we resolve a single entry below instead); for any
235
281
  // other router `draftSource` is.
236
282
  var val = useValStega(valModule);
283
+ var encodes = useEncodesEditTags(valOverlayContext);
237
284
  var draftSource = useDraftModuleSource(valModule && core.Internal.getValPath(valModule) || undefined);
238
285
  var resolvedParams = resolveParams(params);
239
286
  // Careful: null means there was an error - undefined means no params
@@ -263,7 +310,7 @@ function useValRouteStega(selector, params) {
263
310
  return null;
264
311
  }
265
312
  return stega.stegaEncode(content, {
266
- disabled: !valOverlayContext.draftMode,
313
+ disabled: !encodes,
267
314
  root: routeFromVal.getJsonEntryStegaRoot(valModule, url)
268
315
  });
269
316
  }
@@ -1,17 +1,62 @@
1
- import { g as getJsonEntryStegaRoot, _ as _typeof, i as isJsonValuesRecordSchema, a as getValRouteUrlFromVal, b as initValRouteFromVal } from '../../dist/routeFromVal-728295a0.esm.js';
1
+ import { g as getJsonEntryStegaRoot, _ as _typeof, i as isJsonValuesRecordSchema, a as getValRouteUrlFromVal, b as initValRouteFromVal } from '../../dist/routeFromVal-1221e70d.esm.js';
2
2
  import { Internal } from '@valbuild/core';
3
3
  import { getModuleIds, stegaEncode } from '@valbuild/react/stega';
4
4
  import React from 'react';
5
- import { u as useValOverlayContext } from '../../dist/ValOverlayContext-5bc3830f.esm.js';
6
- import '../../dist/createForOfIteratorHelper-485c2ce1.esm.js';
5
+ import { u as useValOverlayContext } from '../../dist/ValOverlayContext-28101df3.esm.js';
6
+ import '../../dist/createForOfIteratorHelper-93a62170.esm.js';
7
7
  import '@valbuild/shared/client';
8
8
  import '@valbuild/core/patch';
9
9
  import 'react/jsx-runtime';
10
10
 
11
+ var subscribeNever = function subscribeNever() {
12
+ return function () {};
13
+ };
14
+
15
+ /**
16
+ * `false` for the server's render and for this component's hydration render,
17
+ * `true` for every render after.
18
+ *
19
+ * Per component, which is the point: React reads `getServerSnapshot` while it
20
+ * hydrates a component and `getSnapshot` otherwise, and re-renders the
21
+ * component right after hydrating it when the two differ. So a component that
22
+ * hydrates late -- a route split out into its own chunk -- still renders its
23
+ * hydration pass as the server did, however long after the rest of the page.
24
+ */
25
+ function useHydrated() {
26
+ return React.useSyncExternalStore(subscribeNever, function () {
27
+ return true;
28
+ }, function () {
29
+ return false;
30
+ });
31
+ }
32
+
33
+ /**
34
+ * Whether the content this component renders carries its edit tags.
35
+ *
36
+ * Always in draft mode -- except, on a page rendered from a server-read draft,
37
+ * for the server's render and for this component's own hydration render: then
38
+ * the draft text goes out without them, and the render React does right after
39
+ * hydrating the component puts them in.
40
+ *
41
+ * Because JSX auto-tagging turns a tagged string into a `data-val-path`
42
+ * attribute as the element is created, and the switch for it is process-wide.
43
+ * The server cannot be relied on to tag (whether the patched JSX runtime is
44
+ * the one it renders with depends on how it was bundled), and no page-wide
45
+ * moment means "everything has hydrated": a split-out route, or a lazy child
46
+ * handed a string as a prop, hydrates after the rest of the page. A string
47
+ * with no tags in it has nothing to tag, wherever it is rendered and whatever
48
+ * the switch says -- so the server's HTML and every hydration render agree.
49
+ */
50
+ function useEncodesEditTags(context) {
51
+ var hydrated = useHydrated();
52
+ return !!context.draftMode && (!context.serverDraft || hydrated);
53
+ }
54
+
11
55
  /** What `useVal` gives back — see `ResolvedVal` in `@valbuild/react/stega`. */
12
56
 
13
57
  function useValStega(selector) {
14
58
  var valOverlayContext = useValOverlayContext();
59
+ var encodes = useEncodesEditTags(valOverlayContext);
15
60
  var moduleIds = React.useMemo(function () {
16
61
  return getModuleIds(selector);
17
62
  }, [selector]);
@@ -64,7 +109,7 @@ function useValStega(selector) {
64
109
  React.use(store.waitForLoad(moduleIds));
65
110
  }
66
111
  return stegaEncode(selector, {
67
- disabled: !valOverlayContext.draftMode,
112
+ disabled: !encodes,
68
113
  getModule: function getModule(moduleId) {
69
114
  if (moduleMap && valOverlayContext.draftMode) {
70
115
  return moduleMap[moduleId];
@@ -151,6 +196,7 @@ var jsonEntryPromiseCache = new Map();
151
196
  */
152
197
  function useValKeyStega(selector, key) {
153
198
  var valOverlayContext = useValOverlayContext();
199
+ var encodes = useEncodesEditTags(valOverlayContext);
154
200
  // A view is a pointer: everything below reads a path, a schema and a source
155
201
  // off this, and a pointer has none of them. See `resolveViewedModule`.
156
202
  var valModule = Internal.resolveViewedModule(selector);
@@ -167,7 +213,7 @@ function useValKeyStega(selector, key) {
167
213
  content = readCommittedJsonEntry(valModule, key);
168
214
  }
169
215
  return stegaEncode(content, {
170
- disabled: !valOverlayContext.draftMode,
216
+ disabled: !encodes,
171
217
  root: getJsonEntryStegaRoot(valModule, key)
172
218
  });
173
219
  }
@@ -226,6 +272,7 @@ function useValRouteStega(selector, params) {
226
272
  // router `val` is unused (we resolve a single entry below instead); for any
227
273
  // other router `draftSource` is.
228
274
  var val = useValStega(valModule);
275
+ var encodes = useEncodesEditTags(valOverlayContext);
229
276
  var draftSource = useDraftModuleSource(valModule && Internal.getValPath(valModule) || undefined);
230
277
  var resolvedParams = resolveParams(params);
231
278
  // Careful: null means there was an error - undefined means no params
@@ -255,7 +302,7 @@ function useValRouteStega(selector, params) {
255
302
  return null;
256
303
  }
257
304
  return stegaEncode(content, {
258
- disabled: !valOverlayContext.draftMode,
305
+ disabled: !encodes,
259
306
  root: getJsonEntryStegaRoot(valModule, url)
260
307
  });
261
308
  }
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var createForOfIteratorHelper = require('./createForOfIteratorHelper-0324e063.cjs.prod.js');
3
+ var createForOfIteratorHelper = require('./createForOfIteratorHelper-9afd7d2d.cjs.prod.js');
4
4
  var patch = require('@valbuild/core/patch');
5
5
  var React = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
@@ -273,6 +273,7 @@ function ValOverlayProvider(_ref) {
273
273
  suspend = _ref.suspend,
274
274
  draftModeReady = _ref.draftModeReady,
275
275
  draftSourcesSynced = _ref.draftSourcesSynced,
276
+ serverDraft = _ref.serverDraft,
276
277
  children = _ref.children;
277
278
  return /*#__PURE__*/jsxRuntime.jsx(ValOverlayContext.Provider, {
278
279
  value: {
@@ -280,7 +281,8 @@ function ValOverlayProvider(_ref) {
280
281
  draftMode: draftMode,
281
282
  suspend: suspend,
282
283
  draftModeReady: draftModeReady,
283
- draftSourcesSynced: draftSourcesSynced
284
+ draftSourcesSynced: draftSourcesSynced,
285
+ serverDraft: serverDraft
284
286
  },
285
287
  children: children
286
288
  });
@@ -1,4 +1,4 @@
1
- import { c as _toPropertyKey, b as _createForOfIteratorHelper, d as _defineProperty } from './createForOfIteratorHelper-485c2ce1.esm.js';
1
+ import { c as _toPropertyKey, b as _createForOfIteratorHelper, d as _defineProperty } from './createForOfIteratorHelper-93a62170.esm.js';
2
2
  import { deepEqual } from '@valbuild/core/patch';
3
3
  import React from 'react';
4
4
  import { jsx } from 'react/jsx-runtime';
@@ -267,6 +267,7 @@ function ValOverlayProvider(_ref) {
267
267
  suspend = _ref.suspend,
268
268
  draftModeReady = _ref.draftModeReady,
269
269
  draftSourcesSynced = _ref.draftSourcesSynced,
270
+ serverDraft = _ref.serverDraft,
270
271
  children = _ref.children;
271
272
  return /*#__PURE__*/jsx(ValOverlayContext.Provider, {
272
273
  value: {
@@ -274,7 +275,8 @@ function ValOverlayProvider(_ref) {
274
275
  draftMode: draftMode,
275
276
  suspend: suspend,
276
277
  draftModeReady: draftModeReady,
277
- draftSourcesSynced: draftSourcesSynced
278
+ draftSourcesSynced: draftSourcesSynced,
279
+ serverDraft: serverDraft
278
280
  },
279
281
  children: children
280
282
  });
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var createForOfIteratorHelper = require('./createForOfIteratorHelper-e75681d7.cjs.dev.js');
3
+ var createForOfIteratorHelper = require('./createForOfIteratorHelper-4497beef.cjs.dev.js');
4
4
  var patch = require('@valbuild/core/patch');
5
5
  var React = require('react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
@@ -273,6 +273,7 @@ function ValOverlayProvider(_ref) {
273
273
  suspend = _ref.suspend,
274
274
  draftModeReady = _ref.draftModeReady,
275
275
  draftSourcesSynced = _ref.draftSourcesSynced,
276
+ serverDraft = _ref.serverDraft,
276
277
  children = _ref.children;
277
278
  return /*#__PURE__*/jsxRuntime.jsx(ValOverlayContext.Provider, {
278
279
  value: {
@@ -280,7 +281,8 @@ function ValOverlayProvider(_ref) {
280
281
  draftMode: draftMode,
281
282
  suspend: suspend,
282
283
  draftModeReady: draftModeReady,
283
- draftSourcesSynced: draftSourcesSynced
284
+ draftSourcesSynced: draftSourcesSynced,
285
+ serverDraft: serverDraft
284
286
  },
285
287
  children: children
286
288
  });
@@ -150,8 +150,10 @@ function _createForOfIteratorHelper(r, e) {
150
150
  };
151
151
  }
152
152
 
153
+ exports._arrayLikeToArray = _arrayLikeToArray;
153
154
  exports._createForOfIteratorHelper = _createForOfIteratorHelper;
154
155
  exports._defineProperty = _defineProperty;
155
156
  exports._objectSpread2 = _objectSpread2;
156
157
  exports._slicedToArray = _slicedToArray;
157
158
  exports._toPropertyKey = _toPropertyKey;
159
+ exports._unsupportedIterableToArray = _unsupportedIterableToArray;
@@ -148,4 +148,4 @@ function _createForOfIteratorHelper(r, e) {
148
148
  };
149
149
  }
150
150
 
151
- export { _objectSpread2 as _, _slicedToArray as a, _createForOfIteratorHelper as b, _toPropertyKey as c, _defineProperty as d };
151
+ export { _objectSpread2 as _, _slicedToArray as a, _createForOfIteratorHelper as b, _toPropertyKey as c, _defineProperty as d, _arrayLikeToArray as e, _unsupportedIterableToArray as f };
@@ -150,8 +150,10 @@ function _createForOfIteratorHelper(r, e) {
150
150
  };
151
151
  }
152
152
 
153
+ exports._arrayLikeToArray = _arrayLikeToArray;
153
154
  exports._createForOfIteratorHelper = _createForOfIteratorHelper;
154
155
  exports._defineProperty = _defineProperty;
155
156
  exports._objectSpread2 = _objectSpread2;
156
157
  exports._slicedToArray = _slicedToArray;
157
158
  exports._toPropertyKey = _toPropertyKey;
159
+ exports._unsupportedIterableToArray = _unsupportedIterableToArray;
@@ -0,0 +1,21 @@
1
+ import type { Json, ModuleFilePath } from "@valbuild/core";
2
+ /**
3
+ * What a draft page shows, read by the server for the request that renders it.
4
+ *
5
+ * The source of every module the editor's draft CHANGES -- the ones with
6
+ * patches applied, published-not-yet-built ones included -- and nothing
7
+ * else: a module without changes is the build's own source, which the page
8
+ * already has. Handed to `<ValProvider draft>` so the server's render and the
9
+ * browser's first render resolve the same text, and the page never shows the
10
+ * published version first and the draft a moment later.
11
+ *
12
+ * A `.jsonValues()` module is in the shape the Studio's own store gives it: the
13
+ * entries the draft edits hold their content, and every other entry is the
14
+ * thunkless marker the hooks resolve from the bundle.
15
+ *
16
+ * Plain JSON, because it travels from a server function to the browser in the
17
+ * page's loader data. `fetchValDraft` in `@valbuild/tanstack/server` reads it.
18
+ */
19
+ export type ValDraft = {
20
+ sources: Record<ModuleFilePath, Json>;
21
+ };
@@ -3,4 +3,5 @@ export declare const ValProvider: (props: {
3
3
  config: import("@valbuild/core").ValConfig;
4
4
  disableRefresh?: boolean;
5
5
  suspend?: boolean;
6
+ draft?: import("./ValDraft.js").ValDraft | null;
6
7
  }) => import("react").JSX.Element;
@@ -15,6 +15,7 @@ export { type SelectorSource, type SelectorOf, GenericSelector, } from "@valbuil
15
15
  export { ValRichText } from "@valbuild/react/internal";
16
16
  export { type ValEncodedString, type File, type Image, type RichText, } from "@valbuild/react/stega";
17
17
  export { ValProvider } from "./ValProvider.js";
18
+ export type { ValDraft } from "./ValDraft.js";
18
19
  export { ValImage, type ValImageProps } from "./ValImage.js";
19
20
  export { ValApp } from "./ValApp.js";
20
21
  export { ValModulesClient, useRegisterValModules } from "./ValModulesClient.js";
@@ -2,6 +2,7 @@ export { initValServer } from "./initValServer.js";
2
2
  export { InMemoryPatchStore, type ValPatchStore } from "@valbuild/server";
3
3
  export type { ValHttpMode } from "./initValServer.js";
4
4
  export { initValContent } from "./initValContent.js";
5
+ export type { ValDraft } from "../ValDraft.js";
5
6
  export { valDraftMode, hasValEnableCookieOnServer, VAL_DRAFT_MODE_COOKIE, type ValDraftMode, } from "./valDraftMode.js";
6
7
  export { initValMcp } from "./initValMcp.js";
7
8
  export { createPrettierFormatter, type PrettierLike, type ValFormatter, } from "@valbuild/server";