@valbuild/next 0.102.0 → 0.103.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 (35) hide show
  1. package/README.md +50 -1
  2. package/client/dist/valbuild-next-client.cjs.dev.js +25 -2
  3. package/client/dist/valbuild-next-client.cjs.prod.js +25 -2
  4. package/client/dist/valbuild-next-client.esm.js +25 -2
  5. package/dist/ValApp-90c4171d.cjs.js +7 -0
  6. package/dist/{ValApp-bb207e96.cjs.prod.js → ValApp-90c4171d.cjs.prod.js} +8 -6
  7. package/dist/{ValApp-1992d8b3.cjs.dev.js → ValApp-c1c3ef55.cjs.dev.js} +8 -6
  8. package/dist/{ValApp-8912fc8b.esm.js → ValApp-e17eb734.esm.js} +5 -3
  9. package/dist/ValCanvasBridge-96f67a41.cjs.dev.js +395 -0
  10. package/dist/ValCanvasBridge-d8713d9d.esm.js +387 -0
  11. package/dist/ValCanvasBridge-f75509c6.cjs.js +7 -0
  12. package/dist/ValCanvasBridge-f75509c6.cjs.prod.js +395 -0
  13. package/dist/{ValNextProvider-7302b8af.esm.js → ValNextProvider-3209948c.esm.js} +282 -43
  14. package/dist/{ValNextProvider-03a9f8fd.cjs.dev.js → ValNextProvider-3d8ebfec.cjs.dev.js} +297 -58
  15. package/dist/ValNextProvider-3fdf8756.cjs.js +7 -0
  16. package/dist/{ValNextProvider-6103cb74.cjs.prod.js → ValNextProvider-3fdf8756.cjs.prod.js} +297 -58
  17. package/dist/ValOverlayContext-1d019b95.cjs.js +7 -0
  18. package/dist/{ValOverlayContext-f7f45bc7.cjs.prod.js → ValOverlayContext-1d019b95.cjs.prod.js} +8 -2
  19. package/dist/{ValOverlayContext-c6f27a6d.cjs.dev.js → ValOverlayContext-ca3b6ebd.cjs.dev.js} +8 -2
  20. package/dist/{ValOverlayContext-3c37e5a7.esm.js → ValOverlayContext-f63e0fc9.esm.js} +8 -2
  21. package/dist/declarations/src/ValApp.d.ts +1 -1
  22. package/dist/declarations/src/ValImage.d.ts +1 -1
  23. package/dist/declarations/src/ValProvider.d.ts +1 -1
  24. package/dist/declarations/src/external_exempt_from_val_quickjs.d.ts +7 -5
  25. package/dist/fallbackColors-5ead2c26.esm.js +58 -0
  26. package/dist/{cssUtils-9c11102f.cjs.dev.js → fallbackColors-9a2ac53a.cjs.dev.js} +30 -0
  27. package/dist/{cssUtils-d97a757c.cjs.prod.js → fallbackColors-d4a5b0da.cjs.prod.js} +30 -0
  28. package/dist/valbuild-next.cjs.dev.js +8 -13
  29. package/dist/valbuild-next.cjs.prod.js +8 -13
  30. package/dist/valbuild-next.esm.js +9 -10
  31. package/package.json +13 -13
  32. package/dist/ValApp-bb207e96.cjs.js +0 -7
  33. package/dist/ValNextProvider-6103cb74.cjs.js +0 -7
  34. package/dist/ValOverlayContext-f7f45bc7.cjs.js +0 -7
  35. package/dist/cssUtils-b5651c03.esm.js +0 -32
package/README.md CHANGED
@@ -42,6 +42,7 @@
42
42
 
43
43
  - [Installation](#installation)
44
44
  - [Getting started](#getting-started)
45
+ - [Previewing unpublished pages](#previewing-unpublished-pages)
45
46
  - [Schema types](#schema-types):
46
47
  - [String](#string)
47
48
  - [Number](#number)
@@ -235,6 +236,50 @@ export type { t } from "@valbuild/next";
235
236
  export { s, c, val, config };
236
237
  ```
237
238
 
239
+ # Previewing unpublished pages
240
+
241
+ An editor who creates a page in the Val editor has not published it yet: the new
242
+ route exists only as a pending change. Your app knows nothing about it, so
243
+ opening that URL hits a `page.tsx` whose content lookup finds no such key — and
244
+ a page component that answers a missing key with `notFound()` shows the editor a
245
+ 404 for the page they just made.
246
+
247
+ `suspend` on `ValProvider` is what makes that page load. Set it when the editor
248
+ should be able to preview unpublished pages:
249
+
250
+ ```tsx
251
+ // ./app/layout.tsx
252
+ <ValProvider config={config} suspend>
253
+ <ValModulesClient />
254
+ {children}
255
+ </ValProvider>
256
+ ```
257
+
258
+ With it set, a component reading content through `useVal`, `useValRoute`,
259
+ `fetchVal` or `fetchValRoute` suspends until the editor's pending changes have
260
+ been applied, instead of resolving against published content and rendering
261
+ something the editor did not ask for. Put a `Suspense` boundary where you want
262
+ the loading state to show; without one, Next uses the nearest `loading.tsx`.
263
+
264
+ **It only ever waits for editors.** Nothing here runs for a visitor: the gate is
265
+ behind the same `val_enable` cookie the editor overlay is, so a normal request
266
+ makes no extra requests, waits for nothing, and renders exactly as it would
267
+ without `suspend`. It is safe to leave on in production, which is the point —
268
+ previewing unpublished content is something editors do against the deployed site.
269
+
270
+ **What it costs an editor.** Pending changes come from the editor's browser, so a
271
+ page opened with the editor active waits for them to arrive before it renders.
272
+ That is the trade: a short wait instead of published content flashing up and
273
+ being replaced, or a 404 for a page that exists.
274
+
275
+ **Known limitation.** A page whose route exists _only_ in an unpublished change
276
+ can still 404 on the very first render: `suspend` is activated in the browser
277
+ after hydration, and the render before that resolves against published content.
278
+ A page that calls `notFound()` at that point cannot recover, since the response
279
+ has already been sent. Reloading the page in the editor works around it. Pages
280
+ that read content for a route they already have — the common case, editing an
281
+ existing page — are not affected.
282
+
238
283
  # Formatting published content
239
284
 
240
285
  If you are using `prettier` or another code formatting tool, it is recommended to setup formatting of code after changes have been applied.
@@ -460,6 +505,8 @@ export default c.define("/app/[slug]/page.val.ts", pageSchema, {
460
505
 
461
506
  To consume a page route from a NextJS "page component", it is recommended you use `fetchValRoute` or `useValRoute`.
462
507
 
508
+ NOTE: a page an editor has created but not published yet is not in the record, so `fetchValRoute` / `useValRoute` return `null` for it and a page component that calls `notFound()` shows the editor a 404. See [previewing unpublished pages](#previewing-unpublished-pages).
509
+
463
510
  NOTE: to be refactor proof (i.e. not break when changing the route), you should always provide the params of the NextJS page component.
464
511
 
465
512
  #### Example fetchValRoute
@@ -730,7 +777,9 @@ import { s, c } from "../val.config";
730
777
 
731
778
  export const schema = s.image();
732
779
 
733
- export default c.define("/image", schema, c.image("/public/myfile.jpg"));
780
+ export default c.define("/image", schema, {
781
+ path: "/public/myfile.jpg",
782
+ });
734
783
  ```
735
784
 
736
785
  **NOTE**: This will not validate, since images requires `width`, `height` and `mimeType`. You can fix validation errors like this by using the CLI or by using the VS Code plugin.
@@ -7,7 +7,7 @@ var routeFromVal = require('../../dist/routeFromVal-b48a454a.cjs.dev.js');
7
7
  var core = require('@valbuild/core');
8
8
  var stega = require('@valbuild/react/stega');
9
9
  var React = require('react');
10
- var ValOverlayContext = require('../../dist/ValOverlayContext-c6f27a6d.cjs.dev.js');
10
+ var ValOverlayContext = require('../../dist/ValOverlayContext-ca3b6ebd.cjs.dev.js');
11
11
  require('../../dist/createForOfIteratorHelper-0445603c.cjs.dev.js');
12
12
  require('../../dist/unsupportedIterableToArray-c8ab77c9.cjs.dev.js');
13
13
  require('../../dist/objectSpread2-58024783.cjs.dev.js');
@@ -46,7 +46,30 @@ function useValStega(selector) {
46
46
  // suspending, -> false only unblocks, and false -> true happens only on an
47
47
  // explicit draft-mode enable which already refreshes the route.
48
48
  // React.use is allowed inside conditionals — it is not a hook.
49
- if (valOverlayContext.suspend && valOverlayContext.draftMode !== false && store && !store.hasAllLoaded(moduleIds)) {
49
+ /**
50
+ * Wait until draft mode is KNOWN, before anything else.
51
+ *
52
+ * `draftMode === null` means `/draft/stat` has not answered yet, and the
53
+ * `getModule` below treats it as off — so a render that slips through here
54
+ * while it is unknown resolves against committed source. For an ordinary field
55
+ * that is a flash of published content; for `useValRoute` on a route that
56
+ * exists only in an uncommitted patch it is `notFound()`, which no later
57
+ * answer can undo. That was the 404 on a page you had just created.
58
+ */
59
+ if (valOverlayContext.suspend && valOverlayContext.draftMode === null && valOverlayContext.draftModeReady) {
60
+ React__default["default"].use(valOverlayContext.draftModeReady);
61
+ }
62
+ /**
63
+ * Then wait for the draft sources — but only while more might be coming.
64
+ *
65
+ * `draftSourcesSynced` is the editor saying it has sent everything it holds,
66
+ * and it only holds modules with patches: an unedited module has no draft, so
67
+ * nothing is ever sent for it. Without that signal this could not tell "not
68
+ * sent yet" from "nothing to send", and waited out `waitForLoad`'s ten second
69
+ * timeout once per unedited module the page reads — which is what left a newly
70
+ * created page sitting on its loading fallback.
71
+ */
72
+ if (valOverlayContext.suspend && valOverlayContext.draftMode !== false && !valOverlayContext.draftSourcesSynced && store && !store.hasAllLoaded(moduleIds)) {
50
73
  React__default["default"].use(store.waitForLoad(moduleIds));
51
74
  }
52
75
  return stega.stegaEncode(selector, {
@@ -7,7 +7,7 @@ var routeFromVal = require('../../dist/routeFromVal-8ac2f984.cjs.prod.js');
7
7
  var core = require('@valbuild/core');
8
8
  var stega = require('@valbuild/react/stega');
9
9
  var React = require('react');
10
- var ValOverlayContext = require('../../dist/ValOverlayContext-f7f45bc7.cjs.prod.js');
10
+ var ValOverlayContext = require('../../dist/ValOverlayContext-1d019b95.cjs.prod.js');
11
11
  require('../../dist/createForOfIteratorHelper-d4afcad8.cjs.prod.js');
12
12
  require('../../dist/unsupportedIterableToArray-0d2087a2.cjs.prod.js');
13
13
  require('../../dist/objectSpread2-13f847a9.cjs.prod.js');
@@ -46,7 +46,30 @@ function useValStega(selector) {
46
46
  // suspending, -> false only unblocks, and false -> true happens only on an
47
47
  // explicit draft-mode enable which already refreshes the route.
48
48
  // React.use is allowed inside conditionals — it is not a hook.
49
- if (valOverlayContext.suspend && valOverlayContext.draftMode !== false && store && !store.hasAllLoaded(moduleIds)) {
49
+ /**
50
+ * Wait until draft mode is KNOWN, before anything else.
51
+ *
52
+ * `draftMode === null` means `/draft/stat` has not answered yet, and the
53
+ * `getModule` below treats it as off — so a render that slips through here
54
+ * while it is unknown resolves against committed source. For an ordinary field
55
+ * that is a flash of published content; for `useValRoute` on a route that
56
+ * exists only in an uncommitted patch it is `notFound()`, which no later
57
+ * answer can undo. That was the 404 on a page you had just created.
58
+ */
59
+ if (valOverlayContext.suspend && valOverlayContext.draftMode === null && valOverlayContext.draftModeReady) {
60
+ React__default["default"].use(valOverlayContext.draftModeReady);
61
+ }
62
+ /**
63
+ * Then wait for the draft sources — but only while more might be coming.
64
+ *
65
+ * `draftSourcesSynced` is the editor saying it has sent everything it holds,
66
+ * and it only holds modules with patches: an unedited module has no draft, so
67
+ * nothing is ever sent for it. Without that signal this could not tell "not
68
+ * sent yet" from "nothing to send", and waited out `waitForLoad`'s ten second
69
+ * timeout once per unedited module the page reads — which is what left a newly
70
+ * created page sitting on its loading fallback.
71
+ */
72
+ if (valOverlayContext.suspend && valOverlayContext.draftMode !== false && !valOverlayContext.draftSourcesSynced && store && !store.hasAllLoaded(moduleIds)) {
50
73
  React__default["default"].use(store.waitForLoad(moduleIds));
51
74
  }
52
75
  return stega.stegaEncode(selector, {
@@ -3,7 +3,7 @@ import { g as getJsonEntryStegaRoot, _ as _typeof, a as isJsonValuesRecordSchema
3
3
  import { Internal } from '@valbuild/core';
4
4
  import { getModuleIds, stegaEncode } from '@valbuild/react/stega';
5
5
  import React from 'react';
6
- import { useValOverlayContext } from '../../dist/ValOverlayContext-3c37e5a7.esm.js';
6
+ import { useValOverlayContext } from '../../dist/ValOverlayContext-f63e0fc9.esm.js';
7
7
  import '../../dist/createForOfIteratorHelper-5758a730.esm.js';
8
8
  import '../../dist/unsupportedIterableToArray-5baabfdc.esm.js';
9
9
  import '../../dist/objectSpread2-60d1bd93.esm.js';
@@ -38,7 +38,30 @@ function useValStega(selector) {
38
38
  // suspending, -> false only unblocks, and false -> true happens only on an
39
39
  // explicit draft-mode enable which already refreshes the route.
40
40
  // React.use is allowed inside conditionals — it is not a hook.
41
- if (valOverlayContext.suspend && valOverlayContext.draftMode !== false && store && !store.hasAllLoaded(moduleIds)) {
41
+ /**
42
+ * Wait until draft mode is KNOWN, before anything else.
43
+ *
44
+ * `draftMode === null` means `/draft/stat` has not answered yet, and the
45
+ * `getModule` below treats it as off — so a render that slips through here
46
+ * while it is unknown resolves against committed source. For an ordinary field
47
+ * that is a flash of published content; for `useValRoute` on a route that
48
+ * exists only in an uncommitted patch it is `notFound()`, which no later
49
+ * answer can undo. That was the 404 on a page you had just created.
50
+ */
51
+ if (valOverlayContext.suspend && valOverlayContext.draftMode === null && valOverlayContext.draftModeReady) {
52
+ React.use(valOverlayContext.draftModeReady);
53
+ }
54
+ /**
55
+ * Then wait for the draft sources — but only while more might be coming.
56
+ *
57
+ * `draftSourcesSynced` is the editor saying it has sent everything it holds,
58
+ * and it only holds modules with patches: an unedited module has no draft, so
59
+ * nothing is ever sent for it. Without that signal this could not tell "not
60
+ * sent yet" from "nothing to send", and waited out `waitForLoad`'s ten second
61
+ * timeout once per unedited module the page reads — which is what left a newly
62
+ * created page sitting on its loading fallback.
63
+ */
64
+ if (valOverlayContext.suspend && valOverlayContext.draftMode !== false && !valOverlayContext.draftSourcesSynced && store && !store.hasAllLoaded(moduleIds)) {
42
65
  React.use(store.waitForLoad(moduleIds));
43
66
  }
44
67
  return stegaEncode(selector, {
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === "production") {
4
+ module.exports = require("./ValApp-90c4171d.cjs.prod.js");
5
+ } else {
6
+ module.exports = require("./ValApp-90c4171d.cjs.dev.js");
7
+ }
@@ -7,7 +7,7 @@ var slicedToArray = require('./slicedToArray-ce613de6.cjs.prod.js');
7
7
  var ui = require('@valbuild/ui');
8
8
  var Script = require('next/script');
9
9
  var React = require('react');
10
- var cssUtils = require('./cssUtils-d97a757c.cjs.prod.js');
10
+ var fallbackColors = require('./fallbackColors-d4a5b0da.cjs.prod.js');
11
11
  var jsxRuntime = require('react/jsx-runtime');
12
12
  require('./unsupportedIterableToArray-0d2087a2.cjs.prod.js');
13
13
  require('@valbuild/shared/internal');
@@ -26,7 +26,7 @@ var ValApp = function ValApp(_ref) {
26
26
  inMessageMode = _useState2[0],
27
27
  setInMessageMode = _useState2[1];
28
28
  var isClientSIde = inMessageMode === undefined;
29
- cssUtils.useConfigStorageSave(config);
29
+ fallbackColors.useConfigStorageSave(config);
30
30
  var container = React.useRef(null);
31
31
  React.useEffect(function () {
32
32
  if (location.search === "?message_onready=true") {
@@ -65,8 +65,10 @@ var ValApp = function ValApp(_ref) {
65
65
  setLoadingTheme(config.defaultTheme);
66
66
  }
67
67
  }, [config]);
68
- var darkBg = "#0c111d";
69
- var lightBg = "white";
68
+ // The studio's canvas, so the loading screen is the same colour as what
69
+ // replaces it rather than a flash of a different one.
70
+ var darkBg = fallbackColors.canvasDarkBg;
71
+ var lightBg = fallbackColors.canvasLightBg;
70
72
  React.useEffect(function () {
71
73
  if (inMessageMode || loadingTheme === null) {
72
74
  return;
@@ -104,7 +106,7 @@ var ValApp = function ValApp(_ref) {
104
106
  minWidth: "100%"
105
107
  },
106
108
  children: [/*#__PURE__*/jsxRuntime.jsx("style", {
107
- children: ".".concat(cssUtils.valPrefixedClass, "animate-spin {\n animation: ").concat(cssUtils.valPrefixedClass, "spin 2s linear infinite;\n}\n@keyframes ").concat(cssUtils.valPrefixedClass, "spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}")
109
+ children: ".".concat(fallbackColors.valPrefixedClass, "animate-spin {\n animation: ").concat(fallbackColors.valPrefixedClass, "spin 2s linear infinite;\n}\n@keyframes ").concat(fallbackColors.valPrefixedClass, "spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}")
108
110
  }), /*#__PURE__*/jsxRuntime.jsx("svg", {
109
111
  xmlns: "http://www.w3.org/2000/svg",
110
112
  width: "24",
@@ -115,7 +117,7 @@ var ValApp = function ValApp(_ref) {
115
117
  strokeWidth: "2",
116
118
  strokeLinecap: "round",
117
119
  strokeLinejoin: "round",
118
- className: cssUtils.cn(["animate-spin"]),
120
+ className: fallbackColors.cn(["animate-spin"]),
119
121
  children: /*#__PURE__*/jsxRuntime.jsx("path", {
120
122
  d: "M21 12a9 9 0 1 1-6.219-8.56"
121
123
  })
@@ -7,7 +7,7 @@ var slicedToArray = require('./slicedToArray-44036a76.cjs.dev.js');
7
7
  var ui = require('@valbuild/ui');
8
8
  var Script = require('next/script');
9
9
  var React = require('react');
10
- var cssUtils = require('./cssUtils-9c11102f.cjs.dev.js');
10
+ var fallbackColors = require('./fallbackColors-9a2ac53a.cjs.dev.js');
11
11
  var jsxRuntime = require('react/jsx-runtime');
12
12
  require('./unsupportedIterableToArray-c8ab77c9.cjs.dev.js');
13
13
  require('@valbuild/shared/internal');
@@ -26,7 +26,7 @@ var ValApp = function ValApp(_ref) {
26
26
  inMessageMode = _useState2[0],
27
27
  setInMessageMode = _useState2[1];
28
28
  var isClientSIde = inMessageMode === undefined;
29
- cssUtils.useConfigStorageSave(config);
29
+ fallbackColors.useConfigStorageSave(config);
30
30
  var container = React.useRef(null);
31
31
  React.useEffect(function () {
32
32
  if (location.search === "?message_onready=true") {
@@ -65,8 +65,10 @@ var ValApp = function ValApp(_ref) {
65
65
  setLoadingTheme(config.defaultTheme);
66
66
  }
67
67
  }, [config]);
68
- var darkBg = "#0c111d";
69
- var lightBg = "white";
68
+ // The studio's canvas, so the loading screen is the same colour as what
69
+ // replaces it rather than a flash of a different one.
70
+ var darkBg = fallbackColors.canvasDarkBg;
71
+ var lightBg = fallbackColors.canvasLightBg;
70
72
  React.useEffect(function () {
71
73
  if (inMessageMode || loadingTheme === null) {
72
74
  return;
@@ -104,7 +106,7 @@ var ValApp = function ValApp(_ref) {
104
106
  minWidth: "100%"
105
107
  },
106
108
  children: [/*#__PURE__*/jsxRuntime.jsx("style", {
107
- children: ".".concat(cssUtils.valPrefixedClass, "animate-spin {\n animation: ").concat(cssUtils.valPrefixedClass, "spin 2s linear infinite;\n}\n@keyframes ").concat(cssUtils.valPrefixedClass, "spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}")
109
+ children: ".".concat(fallbackColors.valPrefixedClass, "animate-spin {\n animation: ").concat(fallbackColors.valPrefixedClass, "spin 2s linear infinite;\n}\n@keyframes ").concat(fallbackColors.valPrefixedClass, "spin {\n 0% {\n transform: rotate(0deg);\n }\n 100% {\n transform: rotate(360deg);\n }\n}")
108
110
  }), /*#__PURE__*/jsxRuntime.jsx("svg", {
109
111
  xmlns: "http://www.w3.org/2000/svg",
110
112
  width: "24",
@@ -115,7 +117,7 @@ var ValApp = function ValApp(_ref) {
115
117
  strokeWidth: "2",
116
118
  strokeLinecap: "round",
117
119
  strokeLinejoin: "round",
118
- className: cssUtils.cn(["animate-spin"]),
120
+ className: fallbackColors.cn(["animate-spin"]),
119
121
  children: /*#__PURE__*/jsxRuntime.jsx("path", {
120
122
  d: "M21 12a9 9 0 1 1-6.219-8.56"
121
123
  })
@@ -3,7 +3,7 @@ import { _ as _slicedToArray } from './slicedToArray-aa291011.esm.js';
3
3
  import { VERSION, VAL_APP_PATH, VAL_APP_ID } from '@valbuild/ui';
4
4
  import Script from 'next/script';
5
5
  import { useState, useRef, useEffect } from 'react';
6
- import { u as useConfigStorageSave, v as valPrefixedClass, c as cn } from './cssUtils-b5651c03.esm.js';
6
+ import { u as useConfigStorageSave, v as valPrefixedClass, c as cn, a as canvasDarkBg, b as canvasLightBg } from './fallbackColors-5ead2c26.esm.js';
7
7
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
8
  import './unsupportedIterableToArray-5baabfdc.esm.js';
9
9
  import '@valbuild/shared/internal';
@@ -57,8 +57,10 @@ var ValApp = function ValApp(_ref) {
57
57
  setLoadingTheme(config.defaultTheme);
58
58
  }
59
59
  }, [config]);
60
- var darkBg = "#0c111d";
61
- var lightBg = "white";
60
+ // The studio's canvas, so the loading screen is the same colour as what
61
+ // replaces it rather than a flash of a different one.
62
+ var darkBg = canvasDarkBg;
63
+ var lightBg = canvasLightBg;
62
64
  useEffect(function () {
63
65
  if (inMessageMode || loadingTheme === null) {
64
66
  return;