@valbuild/server 0.99.1 → 0.100.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.
@@ -1,10 +1,10 @@
1
1
  import { FileMetadata, FileSource, ImageMetadata, ModuleFilePath, PatchId, RemoteSource, Schema, SelectorSource, SerializedSchema, Source, SourcePath, ValConfig, ValModules, ValidationError } from "@valbuild/core";
2
2
  import { result } from "@valbuild/core/fp";
3
- import { ParentRef, Patch, PatchError } from "@valbuild/core/patch";
3
+ import { JSONValue, ParentRef, Patch, PatchError } from "@valbuild/core/patch";
4
4
  import { ValSyntaxError, ValSyntaxErrorTree } from "./patch/ts/syntax.js";
5
5
  import { ParentPatchId } from "@valbuild/core";
6
+ import type { ReifiedRender } from "@valbuild/core";
6
7
  import { ValCommit, ValDeployment } from "@valbuild/shared/internal";
7
- import { ReifiedRender } from "@valbuild/core";
8
8
  export type BaseSha = string & {
9
9
  readonly _tag: unique symbol;
10
10
  };
@@ -72,12 +72,20 @@ export declare abstract class ValOps {
72
72
  schemaSha: SchemaSha;
73
73
  patches?: PatchId[];
74
74
  profileId?: AuthorId;
75
+ /**
76
+ * FS mode only (see ValOpsFS): the fingerprint of the `.jsonValues()` entry
77
+ * FILES the client last saw. Absent in http mode, where content does not
78
+ * change under a running server — a deploy restarts it.
79
+ */
80
+ jsonEntriesSha?: string;
75
81
  } | null): Promise<{
76
82
  type: "request-again" | "no-change" | "did-change";
77
83
  baseSha: BaseSha;
78
84
  schemaSha: SchemaSha;
79
85
  sourcesSha: SourcesSha;
80
86
  patches: PatchId[];
87
+ /** FS mode only — see the `params` counterpart. */
88
+ jsonEntriesSha?: string;
81
89
  } | {
82
90
  type: "use-websocket";
83
91
  url: string;
@@ -96,6 +104,82 @@ export declare abstract class ValOps {
96
104
  private initSources;
97
105
  init(): Promise<void>;
98
106
  getBaseSources(): Promise<Sources>;
107
+ /**
108
+ * Resolves the content of ONE `.jsonValues()` entry.
109
+ *
110
+ * The committed content comes from the entry's import thunk on the base
111
+ * source (so it works in both fs and http mode, with no extra I/O). With
112
+ * `applyPatches` (the default) any pending patches for that entry are then
113
+ * replayed on top, which is what makes draft edits visible to the runtime.
114
+ *
115
+ * Callers that apply patches themselves (the Studio, which owns
116
+ * in-flight client patches the server has not seen) must pass
117
+ * `applyPatches: false` or the same edits would be applied twice.
118
+ */
119
+ getJsonEntry(moduleFilePath: ModuleFilePath, entryKey: string, opts?: {
120
+ applyPatches?: boolean;
121
+ }): Promise<{
122
+ status: "success";
123
+ content: JSONValue | null;
124
+ } | {
125
+ status: "not-found";
126
+ message: string;
127
+ } | {
128
+ status: "error";
129
+ message: string;
130
+ } | {
131
+ status: "unauthorized";
132
+ message: string;
133
+ }>;
134
+ /**
135
+ * Resolves the content of MANY `.jsonValues()` entries in one pass.
136
+ *
137
+ * This is the single implementation; {@link getJsonEntry} is a one-key wrapper.
138
+ * Batching matters because the expensive parts — `initSources()` and
139
+ * `fetchPatches()` — are hoisted OUT of the per-entry loop: resolving 500
140
+ * entries one-by-one would otherwise mean 500 patch fetches.
141
+ *
142
+ * Per-entry problems stay per-entry (`missing` / `errors`) so one corrupt
143
+ * `*.val.json` cannot fail a whole batch. Only a missing or non-record MODULE
144
+ * is a whole-request `not-found`.
145
+ *
146
+ * `selector` is either explicit `keys` or an `offset`/`limit` window over every
147
+ * key of the record, in module key order. The window form requires
148
+ * `applyPatches: false`: enumerating from the base source would silently omit
149
+ * draft-added keys, and a silently-short key list is exactly the class of bug
150
+ * this endpoint exists to avoid.
151
+ */
152
+ getJsonEntries(moduleFilePath: ModuleFilePath, selector: {
153
+ keys: string[];
154
+ } | {
155
+ offset: number;
156
+ limit: number;
157
+ }, opts?: {
158
+ applyPatches?: boolean;
159
+ }): Promise<{
160
+ status: "success";
161
+ entries: {
162
+ key: string;
163
+ content: JSONValue | null;
164
+ }[];
165
+ missing: string[];
166
+ errors: {
167
+ key: string;
168
+ message: string;
169
+ }[];
170
+ total: number;
171
+ offset?: number;
172
+ limit?: number;
173
+ } | {
174
+ status: "not-found";
175
+ message: string;
176
+ } | {
177
+ status: "error";
178
+ message: string;
179
+ } | {
180
+ status: "unauthorized";
181
+ message: string;
182
+ }>;
99
183
  getSchemas(): Promise<Schemas>;
100
184
  getSerializedSchemas(): Promise<Record<ModuleFilePath, SerializedSchema>>;
101
185
  getModuleErrors(): Promise<ModulesError[]>;
@@ -104,6 +188,15 @@ export declare abstract class ValOps {
104
188
  getSourcesSha(): Promise<SourcesSha>;
105
189
  getSchemaSha(): Promise<SchemaSha>;
106
190
  analyzePatches(sortedPatches: OrderedPatches["patches"], commits?: ValCommit[], currentCommitSha?: CommitSha): PatchAnalysis;
191
+ /**
192
+ * Reifies each module's render from its schema INSTANCE.
193
+ *
194
+ * Kept even though the Studio also computes renders client-side: `select` is a
195
+ * user function that lives on the instance and is not part of the serialized
196
+ * schema, so a host app that does not render `<ValModulesClient>` has no
197
+ * instances in the browser and would otherwise get no renders at all. See
198
+ * #470.
199
+ */
107
200
  getRenders(schemas: Schemas, sources: Sources): Promise<{
108
201
  renders: Record<ModuleFilePath, ReifiedRender | null>;
109
202
  }>;
@@ -9,6 +9,11 @@ export declare class ValOpsFS extends ValOps {
9
9
  private static readonly VAL_DIR;
10
10
  private readonly host;
11
11
  constructor(contentUrl: string, rootDir: string, valModules: ValModules, options?: ValOpsOptions);
12
+ /**
13
+ * Change detection for `.jsonValues()` entry files, which no sha can see (their
14
+ * content lives behind a thunk that `JSON.stringify` drops).
15
+ */
16
+ private readonly jsonEntryFilesFingerprint;
12
17
  onInit(): Promise<void>;
13
18
  getPresignedAuthNonce(project: string, corsOrigin: string, auth: {
14
19
  pat: string;
@@ -38,12 +43,14 @@ export declare class ValOpsFS extends ValOps {
38
43
  sourcesSha: SourcesSha;
39
44
  patches: PatchId[];
40
45
  profileId?: AuthorId;
46
+ jsonEntriesSha?: string;
41
47
  } | null): Promise<{
42
48
  type: "request-again" | "no-change" | "did-change";
43
49
  baseSha: BaseSha;
44
50
  schemaSha: SchemaSha;
45
51
  sourcesSha: SourcesSha;
46
52
  patches: PatchId[];
53
+ jsonEntriesSha?: string;
47
54
  } | {
48
55
  type: "use-websocket";
49
56
  url: string;
@@ -0,0 +1,17 @@
1
+ import type { Json, ModuleFilePath } from "@valbuild/core";
2
+ import { ValSourceFileHandler } from "./ValSourceFileHandler.js";
3
+ /**
4
+ * Moves ONE `.jsonValues()` entry that was written inline in the `.val.ts` into
5
+ * its own `*.val.json`, replacing the inline value with
6
+ * `c.json(() => import("./<key>.val.json"))`.
7
+ *
8
+ * This is the fix for the `jsonValues:extract-entry` validation error. It is not
9
+ * expressible as a patch (a patch edits one `.val.ts` and cannot create the
10
+ * backing JSON file), so it writes both files directly — JSON first, so a
11
+ * failure part-way through never leaves the module pointing at a file that does
12
+ * not exist.
13
+ *
14
+ * Root-only, like the rest of the `.jsonValues()` machinery: the entry is looked
15
+ * up in the module's root record/router object literal.
16
+ */
17
+ export declare function extractJsonValuesEntry(moduleFilePath: ModuleFilePath, rootDir: string, entryKey: string, content: Json, sourceFileHandler: ValSourceFileHandler): void;
@@ -1,2 +1,2 @@
1
1
  import { ValidationError } from "@valbuild/core";
2
- export declare function getValidationErrorFileRef(validationError: ValidationError): any;
2
+ export declare function getValidationErrorFileRef(validationError: ValidationError): string | null;
@@ -25,7 +25,9 @@ export { getFileExt } from "./getFileExt.js";
25
25
  export { evalValConfigFile, findAndEvalValConfigFile, } from "./evalValConfigFile.js";
26
26
  export { startValLogin, awaitValLoginConfirmation, persistPersonalAccessToken, ValLoginError, DEFAULT_LOGIN_HOST, DEFAULT_LOGIN_MAX_DURATION, DEFAULT_LOGIN_POLL_INTERVAL, } from "./login.js";
27
27
  export type { ValLoginErrorCode, ValLoginResult, ValLoginSession, } from "./login.js";
28
- export { createModulePathMap, getModulePathRange } from "./modulePathMap.js";
28
+ export { createModulePathMap, createJsonEntryPathMap, getModulePathRange, } from "./modulePathMap.js";
29
+ export { findJsonEntryFilePath } from "./jsonEntryLocation.js";
30
+ export { extractJsonValuesEntry } from "./extractJsonValuesEntry.js";
29
31
  export type { ModulePathMap } from "./modulePathMap.js";
30
32
  export { ValOpsFS } from "./ValOpsFS.js";
31
33
  export { ValOpsHttp } from "./ValOpsHttp.js";
@@ -0,0 +1,17 @@
1
+ import ts from "typescript";
2
+ /**
3
+ * Which `*.val.json` holds a `.jsonValues()` entry's content, given the module's
4
+ * `.val.ts`.
5
+ *
6
+ * Tooling that maps a validation `sourcePath` back to a place in a file needs
7
+ * this: for a jsonValues module the offending value is NOT in the `.val.ts` at
8
+ * all — that file only holds `c.json(() => import("./x.val.json"))` — so a
9
+ * resolver that only ever looks at the `.val.ts` can report the error but not
10
+ * where it lives, which for a record with hundreds of entries is not much of a
11
+ * report.
12
+ *
13
+ * Returns a path relative to the project root (leading slash), or undefined when
14
+ * the module has no such entry (not a jsonValues module, unparseable, or the key
15
+ * is not backed by a `c.json` thunk).
16
+ */
17
+ export declare function findJsonEntryFilePath(moduleFilePath: string, valTsSourceFile: ts.SourceFile, entryKey: string): string | undefined;
@@ -27,3 +27,13 @@ export declare function getModulePathRange(modulePath: string, modulePathMap: Mo
27
27
  };
28
28
  } | undefined;
29
29
  export declare function createModulePathMap(sourceFile: ts.SourceFile): ModulePathMap | undefined;
30
+ /**
31
+ * The {@link createModulePathMap} equivalent for a `.jsonValues()` entry's
32
+ * backing `*.val.json`.
33
+ *
34
+ * The file IS the entry's value, so the map is rooted at the JSON document
35
+ * rather than at a `c.define` argument — but everything below is the same shape,
36
+ * which means a module path like `"title"` (the part after the entry key)
37
+ * resolves against it exactly as it would inside a `.val.ts`.
38
+ */
39
+ export declare function createJsonEntryPathMap(jsonSourceFile: ts.JsonSourceFile): ModulePathMap | undefined;