@valbuild/next 0.135.0 → 0.136.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,58 @@
1
1
  # @valbuild/next
2
2
 
3
+ ## 0.136.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#697](https://github.com/valbuild/val/pull/697) [`5c82928`](https://github.com/valbuild/val/commit/5c82928992d29f133f81cf0704a273c0449b18f1) Thanks [@freekh](https://github.com/freekh)! - Read draft content once per request instead of once per `fetchVal`
8
+
9
+ In draft mode, `fetchVal` resolves its selector by asking the Val server for the
10
+ whole module tree. It did that on every call, so a page with three reads made
11
+ three requests for three identical answers — and `fetchValRouteUrl` added a
12
+ fourth by calling `fetchVal` again on top of the caller's own. The answer cannot
13
+ differ between reads in one request, so it is now read once and shared. On a
14
+ 60-module project a three-read draft render goes from ~187ms to ~70ms.
15
+
16
+ The memo is scoped to the request and nothing wider, because the response it
17
+ holds contains the caller's own unpublished patches: React's `cache()` in a
18
+ Next RSC, a `WeakMap` keyed on the `Request` in TanStack Start, and no caching
19
+ at all where neither is available.
20
+
21
+ What this does **not** change:
22
+
23
+ - **Production is untouched.** With Val disabled the reader never calls the
24
+ server; it resolves against the statically imported module, as before.
25
+ - **What a draft shows is unchanged.** Same query, same patch scoping — a draft
26
+ still shows the caller's own staged work and nobody else's.
27
+ - **The client readers are untouched.** `useVal` already subscribed to exactly
28
+ the modules its selector names.
29
+ - **The server still evaluates every module per request.** Narrowing the
30
+ request's path was measured and does not help: `/sources/~` evaluates,
31
+ previews and validates everything and only then filters the response, so a
32
+ narrow path returns less for the same milliseconds. That remains open.
33
+
34
+ - Updated dependencies [[`cf89e74`](https://github.com/valbuild/val/commit/cf89e7429a79c4304ecbedd4f8b571a0de0f145f), [`c219574`](https://github.com/valbuild/val/commit/c21957498f3c7f4f47cef197c5dc0d591aa744ca), [`e673b43`](https://github.com/valbuild/val/commit/e673b43feaf48b7f30881d6786c858a0cb6a44a2), [`5c82928`](https://github.com/valbuild/val/commit/5c82928992d29f133f81cf0704a273c0449b18f1), [`4450be5`](https://github.com/valbuild/val/commit/4450be570ec3a66d7d98ba273c333ca2d2f163d9), [`d2f385a`](https://github.com/valbuild/val/commit/d2f385a04978992a5036379235e447ba7775faef)]:
35
+ - @valbuild/ui@0.136.1
36
+ - @valbuild/shared@0.136.1
37
+ - @valbuild/server@0.136.1
38
+ - @valbuild/core@0.136.1
39
+ - @valbuild/react@0.136.1
40
+ - @valbuild/language-server@0.136.1
41
+ - @valbuild/mcp@0.136.1
42
+
43
+ ## 0.136.0
44
+
45
+ ### Patch Changes
46
+
47
+ - Updated dependencies [[`f3a4bb7`](https://github.com/valbuild/val/commit/f3a4bb7aea604943b92545c122243798c759715c), [`1d72d00`](https://github.com/valbuild/val/commit/1d72d00a1b036959ae0f1540121701cbb2694dcb)]:
48
+ - @valbuild/ui@0.136.0
49
+ - @valbuild/core@0.136.0
50
+ - @valbuild/server@0.136.0
51
+ - @valbuild/shared@0.136.0
52
+ - @valbuild/language-server@0.136.0
53
+ - @valbuild/react@0.136.0
54
+ - @valbuild/mcp@0.136.0
55
+
3
56
  ## 0.135.0
4
57
 
5
58
  ### Patch Changes
@@ -1,23 +1,69 @@
1
1
  import { type JsonEntryContentOf, type Resolvable, type ResolvedVal, type RouteValueOf } from "@valbuild/react/stega";
2
2
  import { ValConfig, ValModules, type ResolvableModule } from "@valbuild/core";
3
3
  import { cookies, draftMode, headers } from "next/headers";
4
+ import { type RequestScopedMemo } from "@valbuild/shared/internal";
4
5
  import { ValServer } from "@valbuild/server";
5
- declare const initFetchValStega: (config: ValConfig, valApiEndpoints: string, valServerPromise: Promise<ValServer>, isEnabled: () => Promise<boolean>, getHeaders: () => Promise<{
6
+ /**
7
+ * The `modules` map every `fetchVal` in one render shares.
8
+ *
9
+ * Derived from the route's own response type rather than restated, so it cannot
10
+ * drift from what `/sources/~` actually returns.
11
+ */
12
+ export type DraftSources = Extract<Awaited<ReturnType<ValServer["/sources/~"]["PUT"]>>, {
13
+ status: 200;
14
+ }>["json"]["modules"];
15
+ /**
16
+ * The slice of `ValServer` the draft-sources reader uses.
17
+ *
18
+ * Narrower than `ValServer` for the same reason `JsonEntryValServer` below is:
19
+ * it says what the dependency IS, and it lets a test drive the reader with a
20
+ * one-route fake instead of casting a partial object to the whole server type.
21
+ */
22
+ export type DraftSourcesValServer = Pick<ValServer, "/sources/~">;
23
+ /** What the route readers need: the tree, plus the single-entry route. */
24
+ export type RouteReaderValServer = Pick<ValServer, "/sources/~" | "/json">;
25
+ /**
26
+ * Where the reader gets its per-request memo box.
27
+ *
28
+ * Injected rather than reached for, so that "this reader needs a request to
29
+ * scope its cache to" is part of its contract and a test can supply a scope it
30
+ * controls — in jest, React's non-`react-server` build makes `cache` a
31
+ * pass-through, so the real wiring memoises nothing there.
32
+ */
33
+ export type GetDraftSourcesScope = () => Promise<RequestScopedMemo<DraftSources | null> | null>;
34
+ /**
35
+ * A per-request memo box, from React's request memoisation.
36
+ *
37
+ * `cache()` is what makes this safe: in an RSC render React hands back the SAME
38
+ * object for the length of one request and a fresh one for the next. Outside
39
+ * that — a route handler, a test, the non-`react-server` build of React —
40
+ * `cache` is a pass-through, so every call gets its own object, the memo misses,
41
+ * and the reader does exactly what it did before. Ineffective, never shared.
42
+ * See `memoizePerRequest`. `fetchVal` is an RSC API (`SET_RSC(true)` above, and
43
+ * its own error messages say so), so the effective case is the normal one.
44
+ *
45
+ * Built PER `initValRsc` rather than once for the module, because the box is
46
+ * only as specific as the `cache()` it came from: two Val servers in one
47
+ * process sharing one would answer each other's reads within a request, and
48
+ * they hold different content.
49
+ */
50
+ export declare function createReactCacheScope(): GetDraftSourcesScope;
51
+ export declare const initFetchValStega: (config: ValConfig, valApiEndpoints: string, valServerPromise: Promise<DraftSourcesValServer>, isEnabled: () => Promise<boolean>, getHeaders: () => Promise<{
6
52
  get(name: string): string | null;
7
53
  }>, getCookies: () => Promise<{
8
54
  get(name: string): {
9
55
  name: string;
10
56
  value: string;
11
57
  } | undefined;
12
- }>) => <T extends Resolvable>(selector: T) => Promise<ResolvedVal<T>>;
13
- declare const initFetchValRouteStega: (config: ValConfig, valApiEndpoints: string, valServerPromise: Promise<ValServer>, isEnabled: () => Promise<boolean>, getHeaders: () => Promise<{
58
+ }>, getDraftSourcesScope: GetDraftSourcesScope) => <T extends Resolvable>(selector: T) => Promise<ResolvedVal<T>>;
59
+ declare const initFetchValRouteStega: (config: ValConfig, valApiEndpoints: string, valServerPromise: Promise<RouteReaderValServer>, isEnabled: () => Promise<boolean>, getHeaders: () => Promise<{
14
60
  get(name: string): string | null;
15
61
  }>, getCookies: () => Promise<{
16
62
  get(name: string): {
17
63
  name: string;
18
64
  value: string;
19
65
  } | undefined;
20
- }>) => <T extends ResolvableModule>(selector: T, params: Promise<Record<string, string | string[]>> | Record<string, string | string[]> | unknown) => Promise<RouteValueOf<T>>;
66
+ }>, getDraftSourcesScope: GetDraftSourcesScope) => <T extends ResolvableModule>(selector: T, params: Promise<Record<string, string | string[]>> | Record<string, string | string[]> | unknown) => Promise<RouteValueOf<T>>;
21
67
  /**
22
68
  * Loads a single `.jsonValues()` entry's DRAFT content via the in-process
23
69
  * `/json` endpoint (which replays pending patches). Returns `undefined` when the
@@ -75,14 +121,14 @@ export declare const initFetchValKeyStega: (valServerPromise: Promise<JsonEntryV
75
121
  value: string;
76
122
  } | undefined;
77
123
  }>) => <T extends ResolvableModule>(selector: T, key: string) => Promise<JsonEntryContentOf<T> | undefined>;
78
- declare const initFetchValRouteUrl: (config: ValConfig, valApiEndpoints: string, valServerPromise: Promise<ValServer>, isEnabled: () => Promise<boolean>, getHeaders: () => Promise<{
124
+ declare const initFetchValRouteUrl: (config: ValConfig, valApiEndpoints: string, valServerPromise: Promise<RouteReaderValServer>, isEnabled: () => Promise<boolean>, getHeaders: () => Promise<{
79
125
  get(name: string): string | null;
80
126
  }>, getCookies: () => Promise<{
81
127
  get(name: string): {
82
128
  name: string;
83
129
  value: string;
84
130
  } | undefined;
85
- }>) => <T extends ResolvableModule>(selector: T, params?: Promise<Record<string, string | string[]>> | Record<string, string | string[]> | unknown) => Promise<string | null>;
131
+ }>, getDraftSourcesScope: GetDraftSourcesScope) => <T extends ResolvableModule>(selector: T, params?: Promise<Record<string, string | string[]>> | Record<string, string | string[]> | unknown) => Promise<string | null>;
86
132
  type ValNextRscConfig = {
87
133
  draftMode: typeof draftMode;
88
134
  headers: typeof headers;
@@ -12,7 +12,7 @@ var NextImage = require('next/image');
12
12
  var jsxRuntime = require('react/jsx-runtime');
13
13
  var ValApp = require('./ValApp-294e5b53.cjs.dev.js');
14
14
  var ValModulesClient = require('./ValModulesClient-4ecabf99.cjs.dev.js');
15
- var version = require('./version-77226c0a.cjs.dev.js');
15
+ var version = require('./version-f31c6541.cjs.dev.js');
16
16
  var createForOfIteratorHelper = require('./createForOfIteratorHelper-0445603c.cjs.dev.js');
17
17
  require('./defineProperty-f90345d7.cjs.dev.js');
18
18
  require('./unsupportedIterableToArray-c8ab77c9.cjs.dev.js');
@@ -12,7 +12,7 @@ var NextImage = require('next/image');
12
12
  var jsxRuntime = require('react/jsx-runtime');
13
13
  var ValApp = require('./ValApp-ab8a96fd.cjs.prod.js');
14
14
  var ValModulesClient = require('./ValModulesClient-10c0721c.cjs.prod.js');
15
- var version = require('./version-58fd6989.cjs.prod.js');
15
+ var version = require('./version-52797e16.cjs.prod.js');
16
16
  var createForOfIteratorHelper = require('./createForOfIteratorHelper-d4afcad8.cjs.prod.js');
17
17
  require('./defineProperty-8951f469.cjs.prod.js');
18
18
  require('./unsupportedIterableToArray-0d2087a2.cjs.prod.js');
@@ -11,7 +11,7 @@ import NextImage from 'next/image';
11
11
  import { jsx } from 'react/jsx-runtime';
12
12
  export { ValApp } from './ValApp-3a70afe1.esm.js';
13
13
  export { ValModulesClient, useRegisterValModules } from './ValModulesClient-0dd601a1.esm.js';
14
- import { V as VERSION } from './version-12fffb44.esm.js';
14
+ import { V as VERSION } from './version-b626279e.esm.js';
15
15
  import { _ as _createForOfIteratorHelper } from './createForOfIteratorHelper-5758a730.esm.js';
16
16
  import './defineProperty-cca5affa.esm.js';
17
17
  import './unsupportedIterableToArray-5baabfdc.esm.js';
@@ -14,7 +14,7 @@ var packageJson = {
14
14
  "next",
15
15
  "react"
16
16
  ],
17
- version: "0.135.0",
17
+ version: "0.136.1",
18
18
  scripts: {
19
19
  typecheck: "tsc --noEmit",
20
20
  test: "jest"
@@ -12,7 +12,7 @@ var packageJson = {
12
12
  "next",
13
13
  "react"
14
14
  ],
15
- version: "0.135.0",
15
+ version: "0.136.1",
16
16
  scripts: {
17
17
  typecheck: "tsc --noEmit",
18
18
  test: "jest"
@@ -14,7 +14,7 @@ var packageJson = {
14
14
  "next",
15
15
  "react"
16
16
  ],
17
- version: "0.135.0",
17
+ version: "0.136.1",
18
18
  scripts: {
19
19
  typecheck: "tsc --noEmit",
20
20
  test: "jest"
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "next",
13
13
  "react"
14
14
  ],
15
- "version": "0.135.0",
15
+ "version": "0.136.1",
16
16
  "main": "dist/valbuild-next.cjs.js",
17
17
  "module": "dist/valbuild-next.esm.js",
18
18
  "exports": {
@@ -47,13 +47,13 @@
47
47
  "dependencies": {
48
48
  "client-only": "^0.0.1",
49
49
  "server-only": "^0.0.1",
50
- "@valbuild/core": "0.134.0",
51
- "@valbuild/language-server": "0.135.0",
52
- "@valbuild/mcp": "0.135.0",
53
- "@valbuild/react": "0.134.1",
54
- "@valbuild/server": "0.135.0",
55
- "@valbuild/shared": "0.134.1",
56
- "@valbuild/ui": "0.134.0"
50
+ "@valbuild/core": "0.136.1",
51
+ "@valbuild/mcp": "0.136.1",
52
+ "@valbuild/language-server": "0.136.1",
53
+ "@valbuild/react": "0.136.1",
54
+ "@valbuild/server": "0.136.1",
55
+ "@valbuild/shared": "0.136.1",
56
+ "@valbuild/ui": "0.136.1"
57
57
  },
58
58
  "devDependencies": {
59
59
  "@testing-library/react": "^16.3.3",