@valbuild/tanstack 0.124.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.
- package/LICENSE.md +7 -0
- package/README.md +380 -0
- package/client/dist/valbuild-tanstack-client.cjs.d.ts +2 -0
- package/client/dist/valbuild-tanstack-client.cjs.dev.js +288 -0
- package/client/dist/valbuild-tanstack-client.cjs.js +7 -0
- package/client/dist/valbuild-tanstack-client.cjs.prod.js +288 -0
- package/client/dist/valbuild-tanstack-client.esm.js +280 -0
- package/client/package.json +4 -0
- package/dist/ValOverlayContext-87cb022d.esm.js +284 -0
- package/dist/ValOverlayContext-abd6ab9a.cjs.prod.js +292 -0
- package/dist/ValOverlayContext-d3bfacb1.cjs.dev.js +292 -0
- package/dist/createForOfIteratorHelper-0324e063.cjs.prod.js +157 -0
- package/dist/createForOfIteratorHelper-485c2ce1.esm.js +151 -0
- package/dist/createForOfIteratorHelper-e75681d7.cjs.dev.js +157 -0
- package/dist/declarations/src/ValApp.d.ts +6 -0
- package/dist/declarations/src/ValImage.d.ts +24 -0
- package/dist/declarations/src/ValModulesClient.d.ts +34 -0
- package/dist/declarations/src/ValProvider.d.ts +6 -0
- package/dist/declarations/src/ValTypes.d.ts +4 -0
- package/dist/declarations/src/client/index.d.ts +1 -0
- package/dist/declarations/src/client/initValClient.d.ts +27 -0
- package/dist/declarations/src/decodeValPathsOfString.d.ts +2 -0
- package/dist/declarations/src/external_exempt_from_val_quickjs.d.ts +41 -0
- package/dist/declarations/src/getUnpatchedUnencodedVal.d.ts +3 -0
- package/dist/declarations/src/index.d.ts +2 -0
- package/dist/declarations/src/initVal.d.ts +74 -0
- package/dist/declarations/src/server/index.d.ts +5 -0
- package/dist/declarations/src/server/initValContent.d.ts +122 -0
- package/dist/declarations/src/server/initValMcp.d.ts +27 -0
- package/dist/declarations/src/server/initValServer.d.ts +41 -0
- package/dist/declarations/src/server/valDraftMode.d.ts +49 -0
- package/dist/declarations/src/version.d.ts +1 -0
- package/dist/routeFromVal-6693e037.cjs.prod.js +218 -0
- package/dist/routeFromVal-728295a0.esm.js +212 -0
- package/dist/routeFromVal-a9147ceb.cjs.dev.js +218 -0
- package/dist/valbuild-tanstack.cjs.d.ts +2 -0
- package/dist/valbuild-tanstack.cjs.dev.js +2005 -0
- package/dist/valbuild-tanstack.cjs.js +7 -0
- package/dist/valbuild-tanstack.cjs.prod.js +2005 -0
- package/dist/valbuild-tanstack.esm.js +1950 -0
- package/dist/version-1a88fd21.cjs.dev.js +229 -0
- package/dist/version-5ac70ccf.cjs.prod.js +229 -0
- package/dist/version-ac8df696.esm.js +225 -0
- package/package.json +84 -0
- package/server/dist/valbuild-tanstack-server.cjs.d.ts +2 -0
- package/server/dist/valbuild-tanstack-server.cjs.dev.js +1032 -0
- package/server/dist/valbuild-tanstack-server.cjs.js +7 -0
- package/server/dist/valbuild-tanstack-server.cjs.prod.js +1032 -0
- package/server/dist/valbuild-tanstack-server.esm.js +1005 -0
- package/server/package.json +4 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2025 Fredrik Ekholdt and Blank AS
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<p align="center">
|
|
3
|
+
<a href="https://admin.val.build">
|
|
4
|
+
<svg width="100" height="100" viewBox="0 0 944 944" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
5
|
+
<circle cx="472" cy="472" r="472" fill="#1D1C28"/>
|
|
6
|
+
<g filter="url(#filter0_d_14_601)">
|
|
7
|
+
<path d="M181 348C181 345.791 182.791 344 185 344H320C322.209 344 324 345.791 324 348V602C324 604.209 322.209 606 320 606H185C182.791 606 181 604.209 181 602V348Z" fill="#38CD80"/>
|
|
8
|
+
</g>
|
|
9
|
+
<g filter="url(#filter1_i_14_601)">
|
|
10
|
+
<circle cx="252" cy="550" r="24" fill="#1E1F2A"/>
|
|
11
|
+
</g>
|
|
12
|
+
<path d="M659.085 550.374H658.585H654.427C652.095 550.374 650.434 549.729 649.347 548.522C648.25 547.306 647.658 545.431 647.658 542.807V439.857C647.658 437.924 646.091 436.357 644.158 436.357H629.16C627.227 436.357 625.66 437.924 625.66 439.857V455.828C625.66 456.658 624.986 457.332 624.155 457.332C623.593 457.332 623.072 457.015 622.798 456.508C618.502 448.559 612.704 442.661 605.399 438.838C597.962 434.671 589.622 432.592 580.394 432.592C571.897 432.592 563.846 434.128 556.247 437.2C548.643 440.274 541.944 444.796 536.154 450.761L536.153 450.761C530.537 456.552 526.106 463.693 522.854 472.174C519.598 480.668 517.975 490.411 517.975 501.395V505.697C517.975 516.86 519.597 526.693 522.854 535.187C526.105 543.667 530.535 550.895 536.148 556.864L536.153 556.869L536.159 556.875C541.95 562.659 548.647 567.088 556.246 570.161L556.256 570.165C563.856 573.057 572.083 574.5 580.932 574.5C589.456 574.5 597.527 572.325 605.137 567.982C612.625 563.807 618.519 557.469 622.822 548.992C623.085 548.475 623.609 548.147 624.176 548.147H624.546C625.161 548.147 625.66 548.646 625.66 549.261C625.66 555.468 627.583 560.617 631.452 564.665L631.451 564.665L631.46 564.673C635.511 568.72 640.668 570.735 646.889 570.735H658.585H659.085H661.157H661.657H760C761.933 570.735 763.5 569.168 763.5 567.235V553.874C763.5 551.941 761.933 550.374 760 550.374H733.542C732.161 550.374 731.042 549.255 731.042 547.874V385C731.042 383.067 729.475 381.5 727.542 381.5H680.701C678.768 381.5 677.201 383.067 677.201 385V398.361C677.201 400.294 678.768 401.861 680.701 401.861H706.543C707.924 401.861 709.043 402.981 709.043 404.361V547.874C709.043 549.255 707.924 550.374 706.543 550.374H661.657H661.157H659.085ZM600.117 550.146L600.111 550.149C594.977 552.448 589.304 553.601 583.086 553.601C570.468 553.601 560.194 549.435 552.222 541.12C544.436 532.633 540.512 520.847 540.512 505.697V501.395C540.512 494.274 541.581 487.79 543.712 481.936L543.714 481.931C545.849 475.89 548.778 470.842 552.495 466.775C556.398 462.521 560.92 459.246 566.061 456.944C571.195 454.645 576.867 453.492 583.086 453.492C589.117 453.492 594.696 454.731 599.829 457.207L599.838 457.211L599.848 457.215C605.166 459.517 609.681 462.79 613.4 467.035L613.4 467.035L613.408 467.044C617.306 471.292 620.324 476.431 622.458 482.469L622.459 482.474C624.59 488.328 625.66 494.812 625.66 501.933V505.16C625.66 512.46 624.59 519.125 622.458 525.159C620.324 531.022 617.393 536.075 613.669 540.326C609.95 544.571 605.435 547.844 600.117 550.146ZM464.902 570.735C466.39 570.735 467.716 569.794 468.206 568.389L512.685 441.011C513.479 438.736 511.79 436.357 509.38 436.357H491.006C489.496 436.357 488.157 437.325 487.683 438.758L447.951 558.864C447.716 559.575 447.051 560.055 446.303 560.055C445.554 560.055 444.89 559.575 444.655 558.864L404.923 438.758C404.449 437.325 403.109 436.357 401.6 436.357H383.225C380.815 436.357 379.126 438.736 379.921 441.011L424.399 568.389C424.89 569.794 426.215 570.735 427.704 570.735H464.902Z" fill="white" stroke="white"/>
|
|
13
|
+
<defs>
|
|
14
|
+
<filter id="filter0_d_14_601" x="127.464" y="290.464" width="250.072" height="369.072" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
15
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
16
|
+
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
17
|
+
<feOffset/>
|
|
18
|
+
<feGaussianBlur stdDeviation="26.768"/>
|
|
19
|
+
<feComposite in2="hardAlpha" operator="out"/>
|
|
20
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"/>
|
|
21
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_14_601"/>
|
|
22
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_14_601" result="shape"/>
|
|
23
|
+
</filter>
|
|
24
|
+
<filter id="filter1_i_14_601" x="228" y="526" width="48" height="48" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
25
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
26
|
+
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
|
|
27
|
+
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
28
|
+
<feOffset/>
|
|
29
|
+
<feGaussianBlur stdDeviation="6"/>
|
|
30
|
+
<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/>
|
|
31
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0.219608 0 0 0 0 0.803922 0 0 0 0 0.501961 0 0 0 0.3 0"/>
|
|
32
|
+
<feBlend mode="normal" in2="shape" result="effect1_innerShadow_14_601"/>
|
|
33
|
+
</filter>
|
|
34
|
+
</defs>
|
|
35
|
+
</svg>
|
|
36
|
+
</a>
|
|
37
|
+
<br/>
|
|
38
|
+
|
|
39
|
+
# @valbuild/tanstack
|
|
40
|
+
|
|
41
|
+
Val for [TanStack Start](https://tanstack.com/start) (React).
|
|
42
|
+
|
|
43
|
+
Content lives in `.val.ts` files in your repository — type-checked, refactorable,
|
|
44
|
+
reviewable in a pull request — and non-developers edit it in Val Studio, which
|
|
45
|
+
this package mounts at `/val`.
|
|
46
|
+
|
|
47
|
+
## Table of contents
|
|
48
|
+
|
|
49
|
+
- [Installation](#installation)
|
|
50
|
+
- [Wiring it up](#wiring-it-up)
|
|
51
|
+
- [Reading content](#reading-content)
|
|
52
|
+
- [Routes](#routes)
|
|
53
|
+
- [Preview and draft mode](#preview-and-draft-mode)
|
|
54
|
+
- [Images](#images)
|
|
55
|
+
- [Coding agents (MCP)](#coding-agents-mcp)
|
|
56
|
+
- [Differences from `@valbuild/next`](#differences-from-valbuildnext)
|
|
57
|
+
- [Schema reference](#schema-reference)
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
npm install @valbuild/tanstack
|
|
63
|
+
npm install --save-dev @valbuild/cli @valbuild/eslint-plugin
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Requires TanStack Start ≥ 1.130 and React 19.
|
|
67
|
+
|
|
68
|
+
**Tell the route generator that `*.val.ts` files are not routes.** A Val module
|
|
69
|
+
for a route lives beside the route file, and the generator scans everything
|
|
70
|
+
under `src/routes` — so without this it reads `posts.$postId.val.ts` as a route
|
|
71
|
+
at `/posts/$postId/val` and warns on every run:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
// vite.config.ts
|
|
75
|
+
tanstackStart({
|
|
76
|
+
router: { routeFileIgnorePattern: "\\.val\\.[tj]sx?$" },
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
// tsr.config.json — the same, for the standalone `tsr generate` CLI
|
|
82
|
+
{ "routeFileIgnorePattern": "\\.val\\.[tj]sx?$" }
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Wiring it up
|
|
86
|
+
|
|
87
|
+
### `val.config.ts`
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { initVal } from "@valbuild/tanstack";
|
|
91
|
+
|
|
92
|
+
const { s, c, val, config, tanstackRouter, externalPageRouter } = initVal({
|
|
93
|
+
project: "yourorg/your-project", // omit while running locally
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export type { t } from "@valbuild/tanstack";
|
|
97
|
+
export { s, c, val, config, tanstackRouter, externalPageRouter };
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `val.modules.ts`
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
import { modules } from "@valbuild/tanstack";
|
|
104
|
+
import { config } from "./val.config";
|
|
105
|
+
|
|
106
|
+
export default modules(config, [
|
|
107
|
+
{ def: () => import("./src/routes/index.val") },
|
|
108
|
+
{ def: () => import("./src/content/authors.val") },
|
|
109
|
+
]);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `src/val/server.ts` — the API and the server-side readers
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { initValServer, initValContent } from "@valbuild/tanstack/server";
|
|
116
|
+
import { config } from "../../val.config";
|
|
117
|
+
import valModules from "../../val.modules";
|
|
118
|
+
|
|
119
|
+
const { valApiHandler, draftMode } = initValServer(valModules, { ...config });
|
|
120
|
+
|
|
121
|
+
export const {
|
|
122
|
+
fetchValStega: fetchVal,
|
|
123
|
+
fetchValRouteStega: fetchValRoute,
|
|
124
|
+
fetchValKeyStega: fetchValKey,
|
|
125
|
+
fetchValRouteUrl,
|
|
126
|
+
} = initValContent(config, valModules, { draftMode });
|
|
127
|
+
|
|
128
|
+
export { valApiHandler };
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Build both from one `draftMode` object. The API is what turns preview on for a
|
|
132
|
+
browser and the readers are what has to notice; two independently created
|
|
133
|
+
defaults would each work and disagree.
|
|
134
|
+
|
|
135
|
+
### `src/routes/api/val.$.ts` — mount the API
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
139
|
+
import { valApiHandler } from "../../val/server";
|
|
140
|
+
|
|
141
|
+
export const Route = createFileRoute("/api/val/$")({
|
|
142
|
+
server: {
|
|
143
|
+
handlers: {
|
|
144
|
+
GET: ({ request }) => valApiHandler(request),
|
|
145
|
+
POST: ({ request }) => valApiHandler(request),
|
|
146
|
+
PUT: ({ request }) => valApiHandler(request),
|
|
147
|
+
PATCH: ({ request }) => valApiHandler(request),
|
|
148
|
+
DELETE: ({ request }) => valApiHandler(request),
|
|
149
|
+
HEAD: ({ request }) => valApiHandler(request),
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`/api/val/$`, not `/api/val`: every endpoint has a sub-path, so the splat is the
|
|
156
|
+
whole surface.
|
|
157
|
+
|
|
158
|
+
### `src/routes/val/` — Val Studio
|
|
159
|
+
|
|
160
|
+
Three files, because the Studio navigates within itself (it pushes paths like
|
|
161
|
+
`/val/~/...`, which have to resolve to the same page on a reload):
|
|
162
|
+
|
|
163
|
+
```tsx
|
|
164
|
+
// src/routes/val/route.tsx
|
|
165
|
+
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
|
166
|
+
import { ValApp, ValModulesClient } from "@valbuild/tanstack";
|
|
167
|
+
import { config } from "../../../val.config";
|
|
168
|
+
import valModules from "../../../val.modules";
|
|
169
|
+
|
|
170
|
+
export const Route = createFileRoute("/val")({
|
|
171
|
+
component: () => (
|
|
172
|
+
<ValApp config={config}>
|
|
173
|
+
<ValModulesClient modules={valModules} />
|
|
174
|
+
<Outlet />
|
|
175
|
+
</ValApp>
|
|
176
|
+
),
|
|
177
|
+
});
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
// src/routes/val/index.tsx and src/routes/val/$.tsx
|
|
182
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
183
|
+
export const Route = createFileRoute("/val/")({ component: () => null }); // and "/val/$"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### The site's layout — `ValProvider`
|
|
187
|
+
|
|
188
|
+
Put the site's own chrome and `ValProvider` in a **pathless layout route**, not
|
|
189
|
+
in `__root`. `__root` is the shell for every route including `/val`, and the
|
|
190
|
+
Studio should not be rendered inside the site it is editing.
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
// src/routes/_site.tsx
|
|
194
|
+
import { Outlet, createFileRoute } from "@tanstack/react-router";
|
|
195
|
+
import { Suspense } from "react";
|
|
196
|
+
import { ValModulesClient, ValProvider } from "@valbuild/tanstack";
|
|
197
|
+
import { config } from "../../val.config";
|
|
198
|
+
import valModules from "../../val.modules";
|
|
199
|
+
|
|
200
|
+
export const Route = createFileRoute("/_site")({
|
|
201
|
+
component: () => (
|
|
202
|
+
<ValProvider config={config} suspend>
|
|
203
|
+
<ValModulesClient modules={valModules} />
|
|
204
|
+
<Suspense fallback={null}>
|
|
205
|
+
<Outlet />
|
|
206
|
+
</Suspense>
|
|
207
|
+
</ValProvider>
|
|
208
|
+
),
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
A pathless layout adds no URL segment: `_site.index.tsx` is still `/`. Val
|
|
213
|
+
modules named after those files follow the same rule, so
|
|
214
|
+
`_site.posts.$postId.val.ts` holds `/posts/...` keys.
|
|
215
|
+
|
|
216
|
+
**The `<Suspense>` boundary is required when you pass `suspend`.** With none
|
|
217
|
+
between a suspending component and the root, React has nowhere to put a
|
|
218
|
+
fallback and the tree stops updating — which looks like the Studio failing to
|
|
219
|
+
load. TanStack Start provides no boundary of its own.
|
|
220
|
+
|
|
221
|
+
### `src/val/client.ts` — the hooks
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { initValClient } from "@valbuild/tanstack/client";
|
|
225
|
+
import { config } from "../../val.config";
|
|
226
|
+
|
|
227
|
+
export const {
|
|
228
|
+
useValStega: useVal,
|
|
229
|
+
useValRouteStega: useValRoute,
|
|
230
|
+
useValRouteUrl,
|
|
231
|
+
} = initValClient(config);
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Reading content
|
|
235
|
+
|
|
236
|
+
**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
|
+
|
|
241
|
+
```tsx
|
|
242
|
+
function Page() {
|
|
243
|
+
const page = useValRoute(pageVal, Route.useParams());
|
|
244
|
+
const authors = useVal(authorsVal);
|
|
245
|
+
if (page === null) throw notFound();
|
|
246
|
+
return <h1>{page.title}</h1>;
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Read on the server when the content has to exist before the component does** —
|
|
251
|
+
`head`/meta tags, a redirect decided by content, a `notFound()` that must happen
|
|
252
|
+
during the request. Two things to know:
|
|
253
|
+
|
|
254
|
+
1. It has to go through `createServerFn`. A route `loader` runs in the browser
|
|
255
|
+
too — that is what makes a client navigation work — so importing
|
|
256
|
+
`src/val/server.ts` straight into a loader pulls `@valbuild/server`, and
|
|
257
|
+
Node's `fs` with it, into the client bundle. `createServerFn` is compiled
|
|
258
|
+
away on the client and everything only its handler uses goes with it.
|
|
259
|
+
2. Content that arrives through a loader on a first, server-rendered load is not
|
|
260
|
+
click-to-editable. The edit tags are attached as JSX is created, and Val only
|
|
261
|
+
starts attaching them once hydration has told it the Studio is open — by
|
|
262
|
+
which time the component has already rendered its loader data. A client-side
|
|
263
|
+
navigation to the same route tags it normally.
|
|
264
|
+
|
|
265
|
+
```tsx
|
|
266
|
+
const getDoc = createServerFn()
|
|
267
|
+
.validator((params: { slug: string }) => params)
|
|
268
|
+
.handler(async ({ data }) => ({ doc: await fetchValRoute(pageVal, data) }));
|
|
269
|
+
|
|
270
|
+
export const Route = createFileRoute("/_site/docs/$slug")({
|
|
271
|
+
loader: async ({ params }) => {
|
|
272
|
+
const { doc } = await getDoc({ data: params });
|
|
273
|
+
if (!doc) throw notFound();
|
|
274
|
+
return { doc };
|
|
275
|
+
},
|
|
276
|
+
component: Doc,
|
|
277
|
+
});
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Routes
|
|
281
|
+
|
|
282
|
+
A Val module for a route **is named after the route file it sits beside**: the
|
|
283
|
+
`.tsx` becomes `.val.ts`. Its keys are the URLs that route serves.
|
|
284
|
+
|
|
285
|
+
| Route file | Val module | Keys look like |
|
|
286
|
+
| ---------------------------------- | ------------------------------------- | -------------------- |
|
|
287
|
+
| `src/routes/index.tsx` | `src/routes/index.val.ts` | `/` |
|
|
288
|
+
| `src/routes/about.tsx` | `src/routes/about.val.ts` | `/about` |
|
|
289
|
+
| `src/routes/posts.$postId.tsx` | `src/routes/posts.$postId.val.ts` | `/posts/hello-world` |
|
|
290
|
+
| `src/routes/posts/$postId.tsx` | `src/routes/posts/$postId.val.ts` | the same route |
|
|
291
|
+
| `src/routes/docs.$.tsx` (splat) | `src/routes/docs.$.val.ts` | `/docs/a/b` |
|
|
292
|
+
| `src/routes/_site.posts.$id.tsx` | `src/routes/_site.posts.$id.val.ts` | `/posts/1` |
|
|
293
|
+
| `src/routes/(marketing)/about.tsx` | `src/routes/(marketing)/about.val.ts` | `/about` |
|
|
294
|
+
|
|
295
|
+
`.` and `/` both separate segments, `$param` is a parameter, `$` on its own is a
|
|
296
|
+
splat, and `index`, `route`, `(groups)` and `_pathless` layouts contribute no
|
|
297
|
+
URL segment — exactly TanStack Router's own rules.
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
// src/routes/posts.$postId.val.ts
|
|
301
|
+
import { s, c, tanstackRouter } from "../../val.config";
|
|
302
|
+
|
|
303
|
+
export default c.define(
|
|
304
|
+
"/src/routes/posts.$postId.val.ts",
|
|
305
|
+
s.router(tanstackRouter, s.object({ title: s.string() })),
|
|
306
|
+
{ "/posts/hello-world": { title: "Hello world" } },
|
|
307
|
+
);
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
Then, in the route's component, hand `useValRoute` the route's own params
|
|
311
|
+
unchanged — including `_splat`, which is what TanStack calls a splat parameter
|
|
312
|
+
and what Val expects:
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
const post = useValRoute(pageVal, Route.useParams());
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Val validates every key against the route's pattern, so a key that no URL of
|
|
319
|
+
that route could produce is a content error rather than a page that silently
|
|
320
|
+
never renders. Val Studio shows these modules as a sitemap under **Pages**, and
|
|
321
|
+
an editor can add a page there — which creates the key.
|
|
322
|
+
|
|
323
|
+
For links out of your site, `externalPageRouter` takes whole URLs instead.
|
|
324
|
+
|
|
325
|
+
## Preview and draft mode
|
|
326
|
+
|
|
327
|
+
Val's preview shows unpublished edits. TanStack Start has no `draftMode()` of
|
|
328
|
+
its own, so this package brings a cookie (`val_draft_mode`, `valDraftMode()` in
|
|
329
|
+
`@valbuild/tanstack/server`). It is a mode switch, not a credential: every draft
|
|
330
|
+
read also carries Val's session cookie, which is a signed JWT the server
|
|
331
|
+
verifies, so a forged draft cookie gets published content.
|
|
332
|
+
|
|
333
|
+
`suspend` on `ValProvider` makes `useValStega` / `useValRouteStega` wait for
|
|
334
|
+
draft data before rendering, so a page that exists **only** in an unpublished
|
|
335
|
+
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).
|
|
337
|
+
|
|
338
|
+
## Images
|
|
339
|
+
|
|
340
|
+
`ValImage` is a plain `<img>` that keeps the edit tags where the Studio can see
|
|
341
|
+
them, and carries the source's own `width`/`height` and its `hotspot` as
|
|
342
|
+
`object-position`:
|
|
343
|
+
|
|
344
|
+
```tsx
|
|
345
|
+
import { ValImage } from "@valbuild/tanstack";
|
|
346
|
+
|
|
347
|
+
<ValImage src={page.hero.image} style={{ maxWidth: "20rem" }} />;
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Uploads land in `public/val` by default, which Vite serves at `/val/...`. That
|
|
351
|
+
shares a prefix with the Studio route, and Vite's static handling wins, so both
|
|
352
|
+
work — but if you would rather keep them apart, `s.image({ directory })` takes
|
|
353
|
+
another location.
|
|
354
|
+
|
|
355
|
+
## Coding agents (MCP)
|
|
356
|
+
|
|
357
|
+
`initValMcp` from `@valbuild/tanstack/server` gives an MCP host Val's content
|
|
358
|
+
tools — read schemas, look content up, validate it, edit it. It is the same
|
|
359
|
+
implementation `@valbuild/next` uses; mount it on a server route with the MCP
|
|
360
|
+
SDK of your choice. See [`@valbuild/mcp`](https://www.npmjs.com/package/@valbuild/mcp).
|
|
361
|
+
|
|
362
|
+
## Differences from `@valbuild/next`
|
|
363
|
+
|
|
364
|
+
| Next | TanStack Start |
|
|
365
|
+
| ----------------------------------- | -------------------------------------------------- |
|
|
366
|
+
| `nextAppRouter` | `tanstackRouter` |
|
|
367
|
+
| `app/blogs/[blog]/page.val.ts` | `src/routes/blogs.$blog.val.ts` |
|
|
368
|
+
| `initValRsc` (`@valbuild/next/rsc`) | `initValContent` (`@valbuild/tanstack/server`) |
|
|
369
|
+
| `draftMode()` from `next/headers` | `valDraftMode()` (a cookie this package owns) |
|
|
370
|
+
| `valNextAppRouter` | `valApiHandler` — a `Request` in, a `Response` out |
|
|
371
|
+
| `ValImage` wraps `next/image` | `ValImage` is an `<img>` |
|
|
372
|
+
| `fetchVal` in a Server Component | hooks in components; `createServerFn` for loaders |
|
|
373
|
+
|
|
374
|
+
## Schema reference
|
|
375
|
+
|
|
376
|
+
The schema types — `s.string()`, `s.richtext()`, `s.image()`, `s.record()`,
|
|
377
|
+
`s.union()`, `s.keyOf()`, `s.route()`, and the rest — are the same in every Val
|
|
378
|
+
package and are documented in full at
|
|
379
|
+
[val.build/docs](https://val.build/docs) and in
|
|
380
|
+
[`@valbuild/next`'s README](https://github.com/valbuild/val/blob/main/packages/next/README.md#schema-types).
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export * from "../../dist/declarations/src/client/index.js";
|
|
2
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsYnVpbGQtdGFuc3RhY2stY2xpZW50LmNqcy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vZGlzdC9kZWNsYXJhdGlvbnMvc3JjL2NsaWVudC9pbmRleC5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var routeFromVal = require('../../dist/routeFromVal-a9147ceb.cjs.dev.js');
|
|
6
|
+
var core = require('@valbuild/core');
|
|
7
|
+
var stega = require('@valbuild/react/stega');
|
|
8
|
+
var React = require('react');
|
|
9
|
+
var ValOverlayContext = require('../../dist/ValOverlayContext-d3bfacb1.cjs.dev.js');
|
|
10
|
+
require('../../dist/createForOfIteratorHelper-e75681d7.cjs.dev.js');
|
|
11
|
+
require('@valbuild/shared/client');
|
|
12
|
+
require('@valbuild/core/patch');
|
|
13
|
+
require('react/jsx-runtime');
|
|
14
|
+
|
|
15
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
16
|
+
|
|
17
|
+
var React__default = /*#__PURE__*/_interopDefault(React);
|
|
18
|
+
|
|
19
|
+
function useValStega(selector) {
|
|
20
|
+
var valOverlayContext = ValOverlayContext.useValOverlayContext();
|
|
21
|
+
var moduleIds = React__default["default"].useMemo(function () {
|
|
22
|
+
return stega.getModuleIds(selector);
|
|
23
|
+
}, [selector]);
|
|
24
|
+
var store = valOverlayContext.store;
|
|
25
|
+
var moduleMap = React__default["default"].useSyncExternalStore(store ? store.subscribe(moduleIds) : function () {
|
|
26
|
+
return function () {};
|
|
27
|
+
}, store ? store.getSnapshot(moduleIds) : function () {
|
|
28
|
+
return;
|
|
29
|
+
}, store ? store.getServerSnapshot(moduleIds) : function () {
|
|
30
|
+
return;
|
|
31
|
+
});
|
|
32
|
+
// Suspense gate. `suspend` is false during SSR and hydration (so the static
|
|
33
|
+
// committed source is rendered, matching the server HTML exactly) and is
|
|
34
|
+
// activated by ValProvider after hydration — inside a transition — when the
|
|
35
|
+
// `suspend` prop is set AND the Val Enable cookie is present (checked
|
|
36
|
+
// client-side; the server store is never populated). It never deactivates.
|
|
37
|
+
// The production path (no cookie) skips the call entirely. The
|
|
38
|
+
// `draftMode !== false` check is a release valve: with draft mode off the
|
|
39
|
+
// store never receives source updates, so waitForLoad could only ever
|
|
40
|
+
// resolve via its timeout — and would then re-suspend on every subsequent
|
|
41
|
+
// render since the resolved promise is evicted from the cache. draftMode is
|
|
42
|
+
// null until the first /draft/stat poll resolves; null -> true keeps
|
|
43
|
+
// suspending, -> false only unblocks, and false -> true happens only on an
|
|
44
|
+
// explicit draft-mode enable which already refreshes the route.
|
|
45
|
+
// React.use is allowed inside conditionals — it is not a hook.
|
|
46
|
+
/**
|
|
47
|
+
* Wait until draft mode is KNOWN, before anything else.
|
|
48
|
+
*
|
|
49
|
+
* `draftMode === null` means `/draft/stat` has not answered yet, and the
|
|
50
|
+
* `getModule` below treats it as off — so a render that slips through here
|
|
51
|
+
* while it is unknown resolves against committed source. For an ordinary field
|
|
52
|
+
* that is a flash of published content; for `useValRoute` on a route that
|
|
53
|
+
* exists only in an uncommitted patch it is `notFound()`, which no later
|
|
54
|
+
* answer can undo. That was the 404 on a page you had just created.
|
|
55
|
+
*/
|
|
56
|
+
if (valOverlayContext.suspend && valOverlayContext.draftMode === null && valOverlayContext.draftModeReady) {
|
|
57
|
+
React__default["default"].use(valOverlayContext.draftModeReady);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Then wait for the draft sources — but only while more might be coming.
|
|
61
|
+
*
|
|
62
|
+
* `draftSourcesSynced` is the editor saying it has sent everything it holds,
|
|
63
|
+
* and it only holds modules with patches: an unedited module has no draft, so
|
|
64
|
+
* nothing is ever sent for it. Without that signal this could not tell "not
|
|
65
|
+
* sent yet" from "nothing to send", and waited out `waitForLoad`'s ten second
|
|
66
|
+
* timeout once per unedited module the page reads — which is what left a newly
|
|
67
|
+
* created page sitting on its loading fallback.
|
|
68
|
+
*/
|
|
69
|
+
if (valOverlayContext.suspend && valOverlayContext.draftMode !== false && !valOverlayContext.draftSourcesSynced && store && !store.hasAllLoaded(moduleIds)) {
|
|
70
|
+
React__default["default"].use(store.waitForLoad(moduleIds));
|
|
71
|
+
}
|
|
72
|
+
return stega.stegaEncode(selector, {
|
|
73
|
+
disabled: !valOverlayContext.draftMode,
|
|
74
|
+
getModule: function getModule(moduleId) {
|
|
75
|
+
if (moduleMap && valOverlayContext.draftMode) {
|
|
76
|
+
return moduleMap[moduleId];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The module's source as the overlay currently sees it — i.e. WITH the editor's
|
|
84
|
+
* unpublished changes — or undefined when there is no draft view to be had
|
|
85
|
+
* (production, draft mode off, or the overlay has not pushed this module yet).
|
|
86
|
+
*
|
|
87
|
+
* This is what lets the single-entry readers show drafts. They otherwise resolve
|
|
88
|
+
* an entry through its local import thunk, which is the content that was bundled:
|
|
89
|
+
* correct in production, and stale the moment anyone edits in the Studio.
|
|
90
|
+
*/
|
|
91
|
+
function useDraftModuleSource(moduleFilePath) {
|
|
92
|
+
var valOverlayContext = ValOverlayContext.useValOverlayContext();
|
|
93
|
+
var store = valOverlayContext.store;
|
|
94
|
+
var moduleIds = React__default["default"].useMemo(function () {
|
|
95
|
+
return moduleFilePath ? [moduleFilePath] : [];
|
|
96
|
+
}, [moduleFilePath]);
|
|
97
|
+
var moduleMap = React__default["default"].useSyncExternalStore(store ? store.subscribe(moduleIds) : function () {
|
|
98
|
+
return function () {};
|
|
99
|
+
}, store ? store.getSnapshot(moduleIds) : function () {
|
|
100
|
+
return undefined;
|
|
101
|
+
}, store ? store.getServerSnapshot(moduleIds) : function () {
|
|
102
|
+
return undefined;
|
|
103
|
+
});
|
|
104
|
+
if (!valOverlayContext.draftMode || !moduleFilePath) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return moduleMap === null || moduleMap === void 0 ? void 0 : moduleMap[moduleFilePath];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* What the draft view says about one `.jsonValues()` entry.
|
|
112
|
+
*
|
|
113
|
+
* Mirrors the server-side rule in `fetchValKey`/`fetchValRoute`: a draft view
|
|
114
|
+
* that HAS an answer wins, including the answer "this entry is gone", and the
|
|
115
|
+
* bundled content is used only when there is no draft view.
|
|
116
|
+
*/
|
|
117
|
+
function draftJsonEntry(draftSource, key) {
|
|
118
|
+
if (draftSource === undefined || draftSource === null || routeFromVal._typeof(draftSource) !== "object" || Array.isArray(draftSource)) {
|
|
119
|
+
return {
|
|
120
|
+
status: "unavailable"
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
var entry = draftSource[key];
|
|
124
|
+
if (entry === undefined) {
|
|
125
|
+
// The module IS in the draft view and this key is not: it was deleted.
|
|
126
|
+
return {
|
|
127
|
+
status: "absent"
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (core.Internal.isJson(entry)) {
|
|
131
|
+
// An un-loaded marker: the Studio has not fetched this entry's content, so
|
|
132
|
+
// the draft view cannot answer. (The engine asks for entries that pending
|
|
133
|
+
// patches touch, so this resolves itself for anything actually edited.)
|
|
134
|
+
return {
|
|
135
|
+
status: "unavailable"
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
status: "content",
|
|
140
|
+
content: entry
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// The (loosened) content type a single `.jsonValues()` entry resolves to.
|
|
145
|
+
|
|
146
|
+
// Module-level cache of in-flight/resolved entry loads, so `React.use` gets a
|
|
147
|
+
// stable promise across renders (keyed by module path + entry key).
|
|
148
|
+
var jsonEntryPromiseCache = new Map();
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Client counterpart to `fetchValKey`: resolves a SINGLE `.jsonValues()` entry
|
|
152
|
+
* by key, loading only that entry's backing `*.val.json` (one dynamic import).
|
|
153
|
+
* Suspends (via `React.use`) until the entry loads, so it must be rendered
|
|
154
|
+
* inside a `<Suspense>` boundary.
|
|
155
|
+
*
|
|
156
|
+
* In draft mode it renders the editor's unpublished content, taken from the
|
|
157
|
+
* overlay; in production — and whenever there is no draft view — it resolves the
|
|
158
|
+
* entry's lazy import thunk from the local module.
|
|
159
|
+
*/
|
|
160
|
+
function useValKeyStega(selector, key) {
|
|
161
|
+
var valOverlayContext = ValOverlayContext.useValOverlayContext();
|
|
162
|
+
var moduleFilePath = selector && core.Internal.getValPath(selector);
|
|
163
|
+
var draftSource = useDraftModuleSource(moduleFilePath || undefined);
|
|
164
|
+
var draft = draftJsonEntry(draftSource, key);
|
|
165
|
+
if (draft.status === "absent") {
|
|
166
|
+
// Deleted in the draft state. Falling back to the bundled entry here would
|
|
167
|
+
// render content the editor has just removed.
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
var content = draft.status === "content" ? draft.content : undefined;
|
|
171
|
+
if (content === undefined) {
|
|
172
|
+
content = readCommittedJsonEntry(selector, key);
|
|
173
|
+
}
|
|
174
|
+
return stega.stegaEncode(content, {
|
|
175
|
+
disabled: !valOverlayContext.draftMode,
|
|
176
|
+
root: routeFromVal.getJsonEntryStegaRoot(selector, key)
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* The entry's content as bundled: its lazy import thunk, resolved through
|
|
182
|
+
* `React.use` so the caller suspends until it lands.
|
|
183
|
+
*
|
|
184
|
+
* Deliberately not named `use*`: it is called conditionally, which is fine
|
|
185
|
+
* because `React.use` is not a hook, but a `use*` name would read like one.
|
|
186
|
+
*/
|
|
187
|
+
function readCommittedJsonEntry(selector, key) {
|
|
188
|
+
var _Internal$getValPath;
|
|
189
|
+
var source = selector && core.Internal.getSource(selector);
|
|
190
|
+
var marker = source && routeFromVal._typeof(source) === "object" ? source[key] : undefined;
|
|
191
|
+
if (!core.Internal.isJson(marker)) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
var thunk = core.Internal.getJsonImport(marker);
|
|
195
|
+
if (!thunk) {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
var cacheKey = "".concat((_Internal$getValPath = core.Internal.getValPath(selector)) !== null && _Internal$getValPath !== void 0 ? _Internal$getValPath : "", " ").concat(key);
|
|
199
|
+
var promise = jsonEntryPromiseCache.get(cacheKey);
|
|
200
|
+
if (!promise) {
|
|
201
|
+
promise = thunk().then(function (mod) {
|
|
202
|
+
return mod["default"];
|
|
203
|
+
});
|
|
204
|
+
jsonEntryPromiseCache.set(cacheKey, promise);
|
|
205
|
+
}
|
|
206
|
+
return React__default["default"].use(promise);
|
|
207
|
+
}
|
|
208
|
+
function resolveParams(params) {
|
|
209
|
+
if (!params) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
if ("then" in params) {
|
|
213
|
+
// Defensive guard: peerDependencies declare React >=19, but if a consumer
|
|
214
|
+
// somehow ends up on React 18 with a promise params arg, surface a
|
|
215
|
+
// diagnosable error instead of a cryptic `TypeError: React.use is not a
|
|
216
|
+
// function`. Callers treat null as the error sentinel.
|
|
217
|
+
if (!("use" in React__default["default"])) {
|
|
218
|
+
console.error("Val: useValRoute received a Promise params argument but React.use is unavailable. Upgrade to React 19+ or pre-resolve the promise before passing it.");
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return React__default["default"].use(params);
|
|
222
|
+
}
|
|
223
|
+
return params;
|
|
224
|
+
}
|
|
225
|
+
function useValRouteStega(selector, params) {
|
|
226
|
+
var valOverlayContext = ValOverlayContext.useValOverlayContext();
|
|
227
|
+
// Both called unconditionally to keep hook order stable. For a `.jsonValues()`
|
|
228
|
+
// router `val` is unused (we resolve a single entry below instead); for any
|
|
229
|
+
// other router `draftSource` is.
|
|
230
|
+
var val = useValStega(selector);
|
|
231
|
+
var draftSource = useDraftModuleSource(selector && core.Internal.getValPath(selector) || undefined);
|
|
232
|
+
var resolvedParams = resolveParams(params);
|
|
233
|
+
// Careful: null means there was an error - undefined means no params
|
|
234
|
+
if (resolvedParams === null) {
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
var path = selector && core.Internal.getValPath(selector);
|
|
238
|
+
var schema = selector && core.Internal.getSchema(selector);
|
|
239
|
+
// `.jsonValues()` router: map params → the entry key and load ONLY that
|
|
240
|
+
// entry's backing `*.val.json` (one dynamic import), like `useValKey`.
|
|
241
|
+
if (routeFromVal.isJsonValuesRecordSchema(schema)) {
|
|
242
|
+
var source = selector && core.Internal.getSource(selector);
|
|
243
|
+
var url = routeFromVal.getValRouteUrlFromVal(resolvedParams || {}, "useValRoute", path, schema, source);
|
|
244
|
+
if (!url) {
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
var draft = draftJsonEntry(draftSource, url);
|
|
248
|
+
if (draft.status === "absent") {
|
|
249
|
+
// The draft state says this route is gone — see useValKeyStega.
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
var content = draft.status === "content" ? draft.content : undefined;
|
|
253
|
+
if (content === undefined) {
|
|
254
|
+
content = readCommittedJsonEntry(selector, url);
|
|
255
|
+
}
|
|
256
|
+
if (content === undefined) {
|
|
257
|
+
return null;
|
|
258
|
+
}
|
|
259
|
+
return stega.stegaEncode(content, {
|
|
260
|
+
disabled: !valOverlayContext.draftMode,
|
|
261
|
+
root: routeFromVal.getJsonEntryStegaRoot(selector, url)
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
var route = routeFromVal.initValRouteFromVal(resolvedParams || {}, "useValRoute", path, schema, val);
|
|
265
|
+
return route;
|
|
266
|
+
}
|
|
267
|
+
function useValRouteUrl(selector, params) {
|
|
268
|
+
var val = useValStega(selector);
|
|
269
|
+
var resolvedParams = params === undefined ? undefined : resolveParams(params);
|
|
270
|
+
// Careful: null means there was an error - undefined means no params
|
|
271
|
+
if (resolvedParams === null) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
var route = routeFromVal.getValRouteUrlFromVal(resolvedParams || {}, "useValRouteUrl", selector && core.Internal.getValPath(selector), selector && core.Internal.getSchema(selector), val);
|
|
275
|
+
return route;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
279
|
+
function initValClient(config) {
|
|
280
|
+
return {
|
|
281
|
+
useValStega: useValStega,
|
|
282
|
+
useValKeyStega: useValKeyStega,
|
|
283
|
+
useValRouteStega: useValRouteStega,
|
|
284
|
+
useValRouteUrl: useValRouteUrl
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
exports.initValClient = initValClient;
|