@valbuild/server 0.121.0 → 0.122.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/CHANGELOG.md +132 -0
- package/dist/declarations/src/ValOps.d.ts +67 -1
- package/dist/declarations/src/ValOpsFS.d.ts +14 -0
- package/dist/declarations/src/ValOpsHttp.d.ts +30 -0
- package/dist/declarations/src/externalRecords.d.ts +366 -0
- package/dist/declarations/src/fixHandlers.d.ts +13 -0
- package/dist/declarations/src/history/HistoryError.d.ts +90 -0
- package/dist/declarations/src/history/types.d.ts +126 -0
- package/dist/declarations/src/index.d.ts +3 -1
- package/dist/declarations/src/tools/types.d.ts +22 -31
- package/dist/declarations/src/valServerConfig.d.ts +17 -14
- package/dist/valbuild-server.cjs.dev.js +1417 -286
- package/dist/valbuild-server.cjs.prod.js +1417 -286
- package/dist/valbuild-server.esm.js +1414 -289
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,137 @@
|
|
|
1
1
|
# @valbuild/server
|
|
2
2
|
|
|
3
|
+
## 0.122.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#563](https://github.com/valbuild/val/pull/563) [`be32261`](https://github.com/valbuild/val/commit/be32261af19db8018bc37b180d903416018c0b79) Thanks [@freekh](https://github.com/freekh)! - See how a module looked at any past commit, and restore from it by pointing at it
|
|
8
|
+
|
|
9
|
+
Val could publish edits but never look back. Now every commit is a durable
|
|
10
|
+
record you can open, and any part of it can be put back.
|
|
11
|
+
|
|
12
|
+
**Open a commit and the Studio splits in two.** The left half is the Studio
|
|
13
|
+
itself — same navigation, same fields, same everything, because it _is_ the
|
|
14
|
+
editor rather than a copy of it. The right half shows the project as that commit
|
|
15
|
+
left it. On a phone the two become one pane and a toggle.
|
|
16
|
+
|
|
17
|
+
**Restoring is directed: you point at the old value, then at where it goes.**
|
|
18
|
+
Val does not try to work out which of today's fields corresponds to which of the
|
|
19
|
+
commit's. It cannot be sure — array items splice, schemas move — and a restore
|
|
20
|
+
that guesses wrong writes into the wrong place and looks like it worked. Two
|
|
21
|
+
picks leave nothing to guess. You can restore across paths, so last month's
|
|
22
|
+
headline can become today's tagline.
|
|
23
|
+
|
|
24
|
+
Before you click, every field on the "now" side says whether it can hold the
|
|
25
|
+
value you picked, and a field that cannot explains why when you click it rather
|
|
26
|
+
than doing nothing. A changed union is not itself a blocker: what matters is
|
|
27
|
+
whether the value's own shape is still allowed, so a union that gained a case
|
|
28
|
+
restores fine and one that lost the case you are restoring does not.
|
|
29
|
+
|
|
30
|
+
Rich text can be restored but is marked "probably fits" rather than confirmed —
|
|
31
|
+
comparing every mark and block against the options a schema allows is not done
|
|
32
|
+
yet, and saying so is better than a confident answer we cannot back. It is
|
|
33
|
+
checked properly the moment you commit to it: before anything is staged, the old
|
|
34
|
+
value is checked against the field it is going into, and a value that cannot be
|
|
35
|
+
that field is refused with the reason. A value that is the right shape but
|
|
36
|
+
breaks a rule about its content — a name too short for its `minLength` — is
|
|
37
|
+
staged and then held at publish, the same as if you had typed it, because a
|
|
38
|
+
restore should not be stricter than typing.
|
|
39
|
+
|
|
40
|
+
**A whole module can be put back on its own**, from a commit that changed
|
|
41
|
+
several, without reverting the rest of the commit.
|
|
42
|
+
|
|
43
|
+
**Restores are staged, not applied.** They land in pending changes, are reviewed
|
|
44
|
+
beside every other edit, and go out with the next publish. There is also "put
|
|
45
|
+
everything back", for when a whole publish was the mistake.
|
|
46
|
+
|
|
47
|
+
To make this possible, publishing now records each changed module's data and the
|
|
48
|
+
schema it was written against. Not the `.val.ts` — git already keeps that, but
|
|
49
|
+
it is code, and turning code back into data means parsing it, which is
|
|
50
|
+
best-effort and stops working as TypeScript, your runtime and Val move on. The
|
|
51
|
+
schema is kept because a value on its own cannot be drawn: showing a module as it
|
|
52
|
+
was at a commit whose schema has since changed needs _that commit's_ schema, and
|
|
53
|
+
nothing in your current checkout has it.
|
|
54
|
+
|
|
55
|
+
Things it will not pretend about: a module the commit did not touch says so
|
|
56
|
+
rather than showing today's value; a module saved by a different version of Val
|
|
57
|
+
says the version differs and that nothing is lost; a commit made before Val
|
|
58
|
+
started recording history disables restore with the reason next to it. Images
|
|
59
|
+
and files are restored by re-uploading them, since the bytes at an old commit
|
|
60
|
+
may no longer be on your branch.
|
|
61
|
+
|
|
62
|
+
History requires the Val content service. In filesystem mode it reports
|
|
63
|
+
`not-supported-in-fs-mode` rather than faking it from git, which has the files
|
|
64
|
+
but not which of a commit's changes were one editor's work.
|
|
65
|
+
|
|
66
|
+
- [#597](https://github.com/valbuild/val/pull/597) [`5d14612`](https://github.com/valbuild/val/commit/5d14612f612d657a37338136188f2b3c02b28fe7) Thanks [@freekh](https://github.com/freekh)! - MCP: remove personal access token auth. The endpoint now needs an `oauth`
|
|
67
|
+
config, or local filesystem mode.
|
|
68
|
+
|
|
69
|
+
Until now, an MCP endpoint with no `oauth` config accepted whatever bearer token
|
|
70
|
+
a caller presented and relayed it to the Val content backend unread. The
|
|
71
|
+
reasoning was that without an issuer the app has no key to check a token
|
|
72
|
+
against, so it should not pretend to be the authority on what that token may
|
|
73
|
+
do — and that much was right. The shape was not: a credential the app cannot
|
|
74
|
+
check is one it cannot refuse either, so "a deployed endpoint that authenticates
|
|
75
|
+
nobody" was a supported configuration, and an app could serve content-rewriting
|
|
76
|
+
tools without ever being told where its callers should authorize.
|
|
77
|
+
|
|
78
|
+
**If you run Val in proxy mode**, MCP now requires the `oauth` config that
|
|
79
|
+
shipped in `0.120.0`. Callers authorize as themselves against the Val
|
|
80
|
+
authorization server, this app verifies the token's signature, issuer, audience
|
|
81
|
+
and expiry itself, and patches carry the verified profile as their author:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
initValMcp(valModules, config, {
|
|
85
|
+
oauth: {
|
|
86
|
+
issuer: "https://admin.val.build",
|
|
87
|
+
resource: "https://your-app.com/api/mcp",
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Leave it out and the endpoint answers `500` naming the missing config, rather
|
|
93
|
+
than serving the request.
|
|
94
|
+
|
|
95
|
+
**If you run Val in local filesystem mode**, nothing changes. Local development
|
|
96
|
+
still needs no `oauth` config and no authorization server: there is no backend
|
|
97
|
+
to authenticate to, and patches are written with no author. A token presented
|
|
98
|
+
to such a project is still refused rather than ignored — the endpoint answers
|
|
99
|
+
`400` and says to take the credential out of the client's configuration, since
|
|
100
|
+
what it reached was a working tree with no permission check in front of it.
|
|
101
|
+
|
|
102
|
+
Two API changes if you built your own host on `createValTools`:
|
|
103
|
+
|
|
104
|
+
- `ValToolContext.auth` no longer has a `{ type: "pat", pat }` variant.
|
|
105
|
+
`{ type: "verified-profile", profileId, scopes }` is the only credential the
|
|
106
|
+
registry accepts, and `null` still means local filesystem mode.
|
|
107
|
+
- `createValOps` no longer takes an `auth` argument. `ValOpsHttp` still accepts
|
|
108
|
+
a personal access token directly — that is how `val debug` uses the token from
|
|
109
|
+
`val login` — but no server request builds one.
|
|
110
|
+
|
|
111
|
+
Proxy mode also stops keeping one data layer per credential. Each personal
|
|
112
|
+
access token needed its own `ValOpsHttp` to hold it, each of those cached the
|
|
113
|
+
project's evaluated modules, and the bounded cache that kept the memory in
|
|
114
|
+
check turned an eviction into a re-evaluation of every module on the next call.
|
|
115
|
+
Verified callers all share one instance, because they all reach the backend
|
|
116
|
+
under the app's own API key.
|
|
117
|
+
|
|
118
|
+
### Patch Changes
|
|
119
|
+
|
|
120
|
+
- [#618](https://github.com/valbuild/val/pull/618) [`da6794f`](https://github.com/valbuild/val/commit/da6794f3dbd77d49ccfe780b359bab1689ee1b11) Thanks [@freekh](https://github.com/freekh)! - Remove the unused `GET /api/val/session` endpoint.
|
|
121
|
+
|
|
122
|
+
Nothing called it. The Studio reads the profile id from `/stat`, and in proxy
|
|
123
|
+
mode the route proxied to `${VAL_BUILD_URL}/api/val/${project}/auth/session`,
|
|
124
|
+
an upstream route that no longer exists — so calling it by hand returned a 500
|
|
125
|
+
rather than a session. It is gone from both the route declarations in
|
|
126
|
+
`@valbuild/shared` and the implementation in `@valbuild/server`.
|
|
127
|
+
|
|
128
|
+
Session cookie handling itself is unchanged: `/authorize`, `/callback` and
|
|
129
|
+
`/logout` still set and clear `val_session` as before.
|
|
130
|
+
|
|
131
|
+
- Updated dependencies [[`be32261`](https://github.com/valbuild/val/commit/be32261af19db8018bc37b180d903416018c0b79), [`da6794f`](https://github.com/valbuild/val/commit/da6794f3dbd77d49ccfe780b359bab1689ee1b11), [`1c8b7fd`](https://github.com/valbuild/val/commit/1c8b7fda1e84cd8bd32a03a85d2789598b98c3fb)]:
|
|
132
|
+
- @valbuild/shared@0.122.0
|
|
133
|
+
- @valbuild/ui@0.122.0
|
|
134
|
+
|
|
3
135
|
## 0.121.0
|
|
4
136
|
|
|
5
137
|
### Minor Changes
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { MediaSource, FileMetadata, FileSource, ImageMetadata, ModuleFilePath, PatchId, Schema, SelectorSource, SerializedSchema, Source, SourcePath, ValConfig, ValModules, ValidationError } from "@valbuild/core";
|
|
2
2
|
import { result } from "@valbuild/core/fp";
|
|
3
3
|
import { JSONValue, ParentRef, Patch, PatchError } from "@valbuild/core/patch";
|
|
4
|
+
import type { HistoryError } from "./history/HistoryError.js";
|
|
5
|
+
import type { AffectedFile, StoredModuleVersion, CommitPage, CommitPatch, HistoricalCommit } from "./history/types.js";
|
|
4
6
|
import { ValSyntaxError, ValSyntaxErrorTree } from "./patch/ts/syntax.js";
|
|
5
7
|
import { ParentPatchId } from "@valbuild/core";
|
|
6
8
|
import type { ReifiedPreview } from "@valbuild/core";
|
|
@@ -408,10 +410,32 @@ export declare abstract class ValOps {
|
|
|
408
410
|
protected abstract getSourceFile(path: string): Promise<WithGenericError<{
|
|
409
411
|
data: string;
|
|
410
412
|
}>>;
|
|
413
|
+
/**
|
|
414
|
+
* Save a patch's binary file from a `data:...;base64,...` URL.
|
|
415
|
+
*
|
|
416
|
+
* The wire form: `FileReader.readAsDataURL` is what the browser produces, and
|
|
417
|
+
* published `@valbuild/server` versions send it. Code that already HAS bytes
|
|
418
|
+
* should call {@link saveBinaryFileFromPatch} instead of wrapping them in a
|
|
419
|
+
* data URL just to have this unwrap them again.
|
|
420
|
+
*
|
|
421
|
+
* A `null` `data` records a DELETION, which is why this cannot simply be
|
|
422
|
+
* replaced by the byte-taking sibling: there is nothing to hand it.
|
|
423
|
+
*/
|
|
411
424
|
abstract saveBase64EncodedBinaryFileFromPatch(filePath: string, parentRef: ParentRef, patchId: PatchId, data: string | null, type: "file" | "image", metadata: MetadataOfType<"file" | "image"> | undefined): Promise<WithGenericError<{
|
|
412
425
|
patchId: PatchId;
|
|
413
426
|
filePath: string;
|
|
414
427
|
}>>;
|
|
428
|
+
/**
|
|
429
|
+
* The same, for a caller that already has the bytes.
|
|
430
|
+
*
|
|
431
|
+
* Default implementation wraps them back into a data URL so every backend
|
|
432
|
+
* gets this for free; a backend that can take bytes straight through should
|
|
433
|
+
* override it.
|
|
434
|
+
*/
|
|
435
|
+
saveBinaryFileFromPatch(filePath: string, parentRef: ParentRef, patchId: PatchId, bytes: Buffer, mimeType: string, type: "file" | "image", metadata: MetadataOfType<"file" | "image"> | undefined): Promise<WithGenericError<{
|
|
436
|
+
patchId: PatchId;
|
|
437
|
+
filePath: string;
|
|
438
|
+
}>>;
|
|
415
439
|
abstract getBase64EncodedBinaryFileFromPatch(filePath: string, patchId: PatchId, remote: boolean): Promise<Buffer | null>;
|
|
416
440
|
protected abstract getBase64EncodedBinaryFileMetadataFromPatch<T extends "file" | "image">(filePath: string, type: T, patchId: PatchId, remote: boolean): Promise<OpsMetadata<T>>;
|
|
417
441
|
abstract getBinaryFile(filePathOrRef: string): Promise<Buffer | null>;
|
|
@@ -428,6 +452,40 @@ export declare abstract class ValOps {
|
|
|
428
452
|
errors?: undefined;
|
|
429
453
|
deleted?: undefined;
|
|
430
454
|
}>;
|
|
455
|
+
/** One page of a branch's commits, newest first. See history/listCommits. */
|
|
456
|
+
abstract listCommits(branch: string, options?: {
|
|
457
|
+
limit?: number;
|
|
458
|
+
cursor?: string;
|
|
459
|
+
}): Promise<result.Result<CommitPage, HistoryError>>;
|
|
460
|
+
/** The patches that produced one commit, with their ops. */
|
|
461
|
+
abstract getCommitPatches(commitSha: string): Promise<result.Result<{
|
|
462
|
+
commit: HistoricalCommit;
|
|
463
|
+
patches: CommitPatch[];
|
|
464
|
+
}, HistoryError>>;
|
|
465
|
+
/**
|
|
466
|
+
* How each `.val.ts` the commit changed looked BEFORE it, keyed by module
|
|
467
|
+
* file path. Empty for a commit made before this was recorded - which the
|
|
468
|
+
* caller reports as `source-unavailable` rather than as an empty module.
|
|
469
|
+
*/
|
|
470
|
+
/**
|
|
471
|
+
* Each module a commit changed: its data, and the schema it was under.
|
|
472
|
+
*
|
|
473
|
+
* `asOf` widens it from "what this commit changed" to "the whole project as
|
|
474
|
+
* this commit left it", which is what reverting everything to a point in time
|
|
475
|
+
* needs; `moduleFilePath` narrows it to one module, for navigating the
|
|
476
|
+
* history pane off the changed set.
|
|
477
|
+
*/
|
|
478
|
+
abstract getCommitModules(commitSha: string, options?: {
|
|
479
|
+
asOf?: boolean;
|
|
480
|
+
moduleFilePath?: ModuleFilePath;
|
|
481
|
+
}): Promise<result.Result<{
|
|
482
|
+
modules: StoredModuleVersion[];
|
|
483
|
+
complete: boolean;
|
|
484
|
+
}, HistoryError>>;
|
|
485
|
+
/** Which files the commit touched, and how. Names them; does not fetch them. */
|
|
486
|
+
abstract getCommitAffectedFiles(commitSha: string): Promise<result.Result<AffectedFile[], HistoryError>>;
|
|
487
|
+
/** One file's bytes as they were at one commit. */
|
|
488
|
+
abstract getFileAtCommit(commitSha: string, filePath: string, remote: boolean): Promise<result.Result<Buffer, HistoryError>>;
|
|
431
489
|
}
|
|
432
490
|
export type WithGenericError<T extends Record<string, unknown>> = (T & {
|
|
433
491
|
error?: undefined;
|
|
@@ -533,6 +591,15 @@ export type PreparedCommit = {
|
|
|
533
591
|
* Previous source files that were patched
|
|
534
592
|
*/
|
|
535
593
|
previousSourceFiles: Record<ModuleFilePath, string>;
|
|
594
|
+
/**
|
|
595
|
+
* Each changed module's Source after this commit, and its schema.
|
|
596
|
+
*
|
|
597
|
+
* This is what makes a commit restorable. See the comment where it is built.
|
|
598
|
+
*/
|
|
599
|
+
moduleVersions: Record<ModuleFilePath, {
|
|
600
|
+
source: JSONValue | null;
|
|
601
|
+
schema: SerializedSchema;
|
|
602
|
+
}>;
|
|
536
603
|
/**
|
|
537
604
|
* Diagnosis only: what the source file looks like with the appliable patches
|
|
538
605
|
* applied, for modules that had at least one unappliable patch. Populated
|
|
@@ -640,6 +707,5 @@ export type OrderedPatchesMetadata = {
|
|
|
640
707
|
};
|
|
641
708
|
export declare function getFieldsForType<T extends BinaryFileType>(type: T): (keyof MetadataOfType<T> & string)[];
|
|
642
709
|
export declare function createMetadataFromBuffer<T extends BinaryFileType>(type: BinaryFileType, mimeType: string, buffer: Buffer): OpsMetadata<T>;
|
|
643
|
-
export declare function getMimeTypeFromBase64(content: string): string | null;
|
|
644
710
|
export declare function guessMimeTypeFromPath(filePath: string): string | null;
|
|
645
711
|
export declare function bufferFromDataUrl(dataUrl: string): Buffer | undefined;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { PatchId, ModuleFilePath, ValModules } from "@valbuild/core";
|
|
2
2
|
import { AuthorId, BaseSha, BinaryFileType, GenericErrorMessage, MetadataOfType, OpsMetadata, PreparedCommit, ValOps, ValOpsOptions, WithGenericError, SaveSourceFilePatchResult, type PatchGroupMembership, SchemaSha, CommitSha, OrderedPatches, OrderedPatchesMetadata, SourcesSha } from "./ValOps.js";
|
|
3
3
|
import { Patch, ParentRef, ValCommit } from "@valbuild/shared/internal";
|
|
4
|
+
import type { HistoryError } from "./history/HistoryError.js";
|
|
5
|
+
import type { AffectedFile, StoredModuleVersion, CommitPage, CommitPatch, HistoricalCommit } from "./history/types.js";
|
|
6
|
+
import { result } from "@valbuild/core/fp";
|
|
4
7
|
import { Buffer } from "buffer";
|
|
5
8
|
export declare class ValOpsFS extends ValOps {
|
|
6
9
|
private readonly contentUrl;
|
|
@@ -158,4 +161,15 @@ export declare class ValOpsFS extends ValOps {
|
|
|
158
161
|
* whole directory, and a lock that moves away with it is not holding anything.
|
|
159
162
|
*/
|
|
160
163
|
private getPatchLockFile;
|
|
164
|
+
listCommits(): Promise<result.Result<CommitPage, HistoryError>>;
|
|
165
|
+
getCommitPatches(): Promise<result.Result<{
|
|
166
|
+
commit: HistoricalCommit;
|
|
167
|
+
patches: CommitPatch[];
|
|
168
|
+
}, HistoryError>>;
|
|
169
|
+
getCommitModules(): Promise<result.Result<{
|
|
170
|
+
modules: StoredModuleVersion[];
|
|
171
|
+
complete: boolean;
|
|
172
|
+
}, HistoryError>>;
|
|
173
|
+
getCommitAffectedFiles(): Promise<result.Result<AffectedFile[], HistoryError>>;
|
|
174
|
+
getFileAtCommit(): Promise<result.Result<Buffer, HistoryError>>;
|
|
161
175
|
}
|
|
@@ -2,7 +2,10 @@ import { type PatchId, type ModuleFilePath, ValModules } from "@valbuild/core";
|
|
|
2
2
|
import type { Patch as PatchT, ParentRef as ParentRefT } from "@valbuild/core/patch";
|
|
3
3
|
import { type AuthorId, type BaseSha, BinaryFileType, type CommitSha, GenericErrorMessage, MetadataOfType, OpsMetadata, PreparedCommit, ValOps, ValOpsOptions, WithGenericError, SaveSourceFilePatchResult, type PatchGroupMembership, SchemaSha, OrderedPatchesMetadata, OrderedPatches, SourcesSha } from "./ValOps.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
|
+
import type { HistoryError } from "./history/HistoryError.js";
|
|
6
|
+
import type { AffectedFile, StoredModuleVersion, CommitPage, CommitPatch, HistoricalCommit } from "./history/types.js";
|
|
5
7
|
import { ParentRef, ValCommit, ValDeployment, type PatchGroupT } from "@valbuild/shared/internal";
|
|
8
|
+
import { result } from "@valbuild/core/fp";
|
|
6
9
|
declare const PatchId: z.ZodString & z.ZodType<PatchId, string, z.core.$ZodTypeInternals<PatchId, string>>;
|
|
7
10
|
declare const CommitSha: z.ZodString & z.ZodType<CommitSha, string, z.core.$ZodTypeInternals<CommitSha, string>>;
|
|
8
11
|
declare const BaseSha: z.ZodString & z.ZodType<BaseSha, string, z.core.$ZodTypeInternals<BaseSha, string>>;
|
|
@@ -281,5 +284,32 @@ export declare class ValOpsHttp extends ValOps {
|
|
|
281
284
|
isNotFastForward?: boolean;
|
|
282
285
|
error: GenericErrorMessage;
|
|
283
286
|
}>;
|
|
287
|
+
/**
|
|
288
|
+
* One GET against the content service, parsed and Result-typed.
|
|
289
|
+
*
|
|
290
|
+
* Every history read has the same three failure modes - could not reach the
|
|
291
|
+
* service, the commit is not there, the answer was not what was expected -
|
|
292
|
+
* and each of them means something different to a caller deciding whether to
|
|
293
|
+
* offer a restore. Doing it once here is what keeps that consistent across
|
|
294
|
+
* the five endpoints.
|
|
295
|
+
*/
|
|
296
|
+
private getHistory;
|
|
297
|
+
listCommits(branch: string, options?: {
|
|
298
|
+
limit?: number;
|
|
299
|
+
cursor?: string;
|
|
300
|
+
}): Promise<result.Result<CommitPage, HistoryError>>;
|
|
301
|
+
getCommitPatches(commitSha: string): Promise<result.Result<{
|
|
302
|
+
commit: HistoricalCommit;
|
|
303
|
+
patches: CommitPatch[];
|
|
304
|
+
}, HistoryError>>;
|
|
305
|
+
getCommitModules(commitSha: string, options?: {
|
|
306
|
+
asOf?: boolean;
|
|
307
|
+
moduleFilePath?: ModuleFilePath;
|
|
308
|
+
}): Promise<result.Result<{
|
|
309
|
+
modules: StoredModuleVersion[];
|
|
310
|
+
complete: boolean;
|
|
311
|
+
}, HistoryError>>;
|
|
312
|
+
getCommitAffectedFiles(commitSha: string): Promise<result.Result<AffectedFile[], HistoryError>>;
|
|
313
|
+
getFileAtCommit(commitSha: string, filePath: string, remote: boolean): Promise<result.Result<Buffer, HistoryError>>;
|
|
284
314
|
}
|
|
285
315
|
export {};
|