Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c6df91c
Fix validation worker loading issue
freekh Jun 24, 2026
88dd4de
Changeset
freekh Jun 24, 2026
33f775c
Revert the maxLength in the example causing validation issues
freekh Jun 24, 2026
0fcfecf
Fix issue where keyOf and router stopped publishing from working
freekh Jun 24, 2026
08209c6
Version Packages
github-actions[bot] Jun 24, 2026
c5b812a
Merge pull request #447 from valbuild/changeset-release/main
freekh Jun 24, 2026
be33f33
Use type-only import for ValidationWorkerFactory in ValSyncEngine
freekh Jun 24, 2026
539ae72
Use type-only import from @valbuild/core in ValidationWorkerClient
freekh Jun 24, 2026
cb8360e
Split runtime and type-only imports in validateModule
freekh Jun 24, 2026
b1429d2
Merge pull request #446 from valbuild/fix-validation-worker-loading-i…
freekh Jun 24, 2026
0f635b1
Version Packages
github-actions[bot] Jun 24, 2026
80fd559
Merge pull request #449 from valbuild/changeset-release/main
freekh Jun 24, 2026
7947ac6
Initialize support for jsonValues
freekh Jun 27, 2026
6cd29ed
Add server-side jsonValues content validation + eager resolver
freekh Jun 28, 2026
708a7ed
Add jsonValues validation sha helper (schema-hash prefix)
freekh Jun 28, 2026
69b9324
Add single-entry runtime read API for jsonValues (fetchValKey/useValKey)
freekh Jun 28, 2026
2b57c42
Add example using jsonValues
freekh Jun 28, 2026
55a15e8
Add back project
freekh Jun 28, 2026
cd37b0a
Add /json single-entry endpoint + jsonValues example (support pages)
freekh Jun 29, 2026
5eef908
Record Studio UI lazy-load plan + expected resolvePath-guard note
freekh Jun 29, 2026
7590937
Studio: lazy-load jsonValues entry content (consume /json)
freekh Jun 29, 2026
cf7b0ce
Tracker: Studio lazy-load done; editing needs commit flow
freekh Jun 29, 2026
32f0292
ix jsonValues markers leaking into Studio source/schema walkers
freekh Jun 29, 2026
25f6beb
Remove c.json sha parameter...\n... Wanted to have a good way where w…
freekh Jul 2, 2026
1ac3b15
Persist json values patch edits
freekh Jul 2, 2026
22729d0
Enhance jsonValues support: implement fetchValRoute for dynamic entry…
freekh Jul 5, 2026
b62b9a2
jsonValues: fix Studio round-trip (rename, publish, error state, nest…
freekh Jul 27, 2026
4124b86
Revert maxLength on blog titles (used for testing validation errors)
freekh Jul 27, 2026
7b8b25a
jsonValues: load a pending-renamed entry under its base key
freekh Jul 27, 2026
d6bf5e2
Merge remote-tracking branch 'origin/main' into json-values
freekh Jul 28, 2026
332efc0
jsonValues: stop entry-content patches poisoning getSources
freekh Jul 28, 2026
f5cdcc9
jsonValues: make /json draft-aware
freekh Jul 28, 2026
91a56a4
jsonValues: give entries edit tags + read drafts in the RSC path
freekh Jul 28, 2026
20905c0
Tracker: draft runtime path + edit tags done; client hooks still comm…
freekh Jul 28, 2026
80aa412
Tracker: add Phase 6 — reference integrity, list view, search over un…
freekh Jul 30, 2026
acd40aa
jsonValues: batch /json (Phase 6 step 1)
freekh Jul 31, 2026
dbdaa1c
jsonValues: batch entry loading in the sync engine (Phase 6 step 2)
freekh Jul 31, 2026
5348252
jsonValues: virtualize record lists, and stop the N-request storm (Ph…
freekh Jul 31, 2026
229a05b
jsonValues: schema-only load predicate (Phase 6 step 4)
freekh Jul 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
945 changes: 945 additions & 0 deletions docs/plans/jsonValues.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions examples/next/app/blogs/[blog]/page.val.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import authorsVal from "../../../content/authors.val";
import { linkSchema } from "../../../components/link.val";

const blogSchema = s.object({
title: s.string().maxLength(3),
title: s.string(),
content: s.richtext({
inline: {
a: s.route(),
Expand Down Expand Up @@ -228,7 +228,7 @@ export default c.define(
},
},
"/blogs/blog-15": {
title: "Blog 1",
title: "Blog dette er jo sinnsyjt! men hvorfor har vi to feil?",
content: [
{
tag: "p",
Expand Down
5 changes: 5 additions & 0 deletions examples/next/app/support/[slug]/content/faq.val.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "FAQ",
"body": "Frequently asked questions about Val. Each support page is its own *.val.json so the router scales to many pages.",
"order": 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "Getting started",
"body": "Welcome to Val support. This page is stored as a separate JSON file and loaded lazily via .jsonValues().",
"order": 1
}
21 changes: 21 additions & 0 deletions examples/next/app/support/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { fetchValRoute } from "../../../val/rsc";
import supportVal from "./page.val";

export default async function SupportPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
// Maps the route params to the matching entry and loads ONLY that entry's
// backing *.val.json (one dynamic import), not the whole support-pages record.
const page = await fetchValRoute(supportVal, params);
if (!page) {
return <main>Support page not found.</main>;
}
return (
<main>
<h1>{page.title}</h1>
<p>{page.body}</p>
</main>
);
}
25 changes: 25 additions & 0 deletions examples/next/app/support/[slug]/page.val.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { s, c, type t, nextAppRouter } from "../../../val.config";

/**
* Support pages use `.jsonValues()`: each page's content lives in its own
* `*.val.json` file and is loaded lazily via `c.json(() => import(...))`.
* This keeps `page.val.ts` tiny even with thousands of support pages.
*/
export const supportPageSchema = s.object({
title: s.string().minLength(2),
body: s.string(),
order: s.number(),
});

export type SupportPage = t.inferSchema<typeof supportPageSchema>;

export default c.define(
"/app/support/[slug]/page.val.ts",
s.router(nextAppRouter, supportPageSchema).jsonValues(),
{
"/support/getting-started": c.json(
() => import("./content/getting-started.val.json"),
),
"/support/faq": c.json(() => import("./content/faq.val.json")),
},
);
2 changes: 1 addition & 1 deletion examples/next/val.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { initVal } from "@valbuild/next";

const { s, c, val, config, nextAppRouter, externalPageRouter } = initVal({
// project: "valbuild/val-examples-next",
project: "valbuild/val-examples-next",
root: "/examples/next",
defaultTheme: "dark",
ai: {
Expand Down
1 change: 1 addition & 0 deletions examples/next/val.modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { config } from "./val.config";
export default modules(config, [
{ def: () => import("./content/authors.val") },
{ def: () => import("./app/blogs/[blog]/page.val") },
{ def: () => import("./app/support/[slug]/page.val") },
{ def: () => import("./app/generic/[[...path]]/page.val") },
{ def: () => import("./content/media.val") },
{ def: () => import("./app/page.val") },
Expand Down
3 changes: 2 additions & 1 deletion examples/next/val/rsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { cookies, draftMode, headers } from "next/headers";

const {
fetchValStega: fetchVal,
fetchValKeyStega: fetchValKey,
fetchValRouteStega: fetchValRoute,
fetchValRouteUrl,
} = initValRsc(config, valModules, {
Expand All @@ -14,4 +15,4 @@ const {
cookies,
});

export { fetchVal, fetchValRoute, fetchValRouteUrl };
export { fetchVal, fetchValKey, fetchValRoute, fetchValRouteUrl };
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@valbuild/cli",
"private": false,
"version": "0.97.1",
"version": "0.97.3",
"description": "Val CLI tools",
"repository": {
"type": "git",
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type { FileMetadata } from "./schema/file";
export type { ValModule, SerializedModule, InferValModuleType } from "./module";
export type { SourceObject, SourcePrimitive, Source } from "./source";
export type { FileSource } from "./source/file";
export type { JsonSource, JsonOf, JsonImportThunk } from "./source/json";
export type { RemoteSource, RemoteRef } from "./source/remote";
export { DEFAULT_VAL_REMOTE_HOST } from "./schema/remote";
export type { RawString } from "./schema/string";
Expand Down Expand Up @@ -98,6 +99,7 @@ import {
import { type ImageMetadata } from "./schema/image";
import { type FileMetadata } from "./schema/file";
import { isFile } from "./source/file";
import { isJson, getJsonImport, resolveJsonValues } from "./source/json";
import { createRemoteRef } from "./source/remote";
import {
getValidationBasis,
Expand Down Expand Up @@ -197,6 +199,9 @@ const Internal = {
},
isVal,
isFile,
isJson,
getJsonImport,
resolveJsonValues,
createValPathOfItem,
getSHA256Hash,
initSchema,
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/initVal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { InitSchema, initSchema } from "./initSchema";
import { getValPath as getPath } from "./val";
import { initFile } from "./source/file";
import { initImage } from "./source/image";
import { json } from "./source/json";
import { initRemote } from "./source/remote";
// import { i18n, I18n } from "./source/future/i18n";
// import { remote } from "./source/future/remote";
Expand All @@ -13,6 +14,7 @@ export type ContentConstructor = {
file: ReturnType<typeof initFile>;
image: ReturnType<typeof initImage>;
remote: ReturnType<typeof initRemote>;
json: typeof json;
};
export type ValConstructor = {
unstable_getPath: typeof getPath;
Expand Down Expand Up @@ -96,6 +98,7 @@ InitVal => {
remote: initRemote(config),
file: initFile(config),
image: initImage(config),
json,
},
s,
config,
Expand Down
18 changes: 18 additions & 0 deletions packages/core/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
SerializedImageSchema,
} from "./schema/image";
import { FILE_REF_PROP, FileSource } from "./source/file";
import { isJson } from "./source/json";
import { AllRichTextOptions, RichTextSource } from "./source/richtext";
import { RecordSchema, SerializedRecordSchema } from "./schema/record";
import { RawString } from "./schema/string";
Expand Down Expand Up @@ -342,6 +343,15 @@ export function resolvePath<
resolvedSchema instanceof RecordSchema
? resolvedSchema?.["item"]
: resolvedSchema.item;
// A `.jsonValues()` entry value is a lazily-loaded JsonSource marker. We
// can resolve the entry itself (schema is now `item`), but we cannot
// descend deeper until the backing `*.val.json` content has been loaded
// and substituted into the source tree (done by the UI/server layer).
if (isJson(resolvedSource) && parts.length > 0) {
throw Error(
`Cannot resolve path into a jsonValues entry until its content is loaded. Path: ${path}`,
);
}
} else if (isObjectSchema(resolvedSchema)) {
if (typeof resolvedSource !== "object") {
throw Error(
Expand Down Expand Up @@ -593,6 +603,14 @@ export function safeResolvePath<
resolvedSchema instanceof RecordSchema
? resolvedSchema?.["item"]
: resolvedSchema.item;
// jsonValues entry: cannot descend deeper until the backing `*.val.json`
// content is loaded into the source tree.
if (isJson(resolvedSource) && parts.length > 0) {
return {
status: "error",
message: `Cannot resolve path into a jsonValues entry until its content is loaded. Path: ${path}`,
};
}
} else if (isObjectSchema(resolvedSchema)) {
if (resolvedSource === undefined) {
return {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/schema/deserialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ function deserializeSchemaImpl(
false,
false,
serialized.description,
serialized.jsonValues ?? false,
);
case "keyOf":
return new KeyOfSchema(
Expand Down
Loading
Loading