@valbuild/server 0.146.4 → 0.148.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 +129 -0
- package/browser/dist/valbuild-server-browser.cjs.d.ts +2 -0
- package/browser/dist/valbuild-server-browser.cjs.dev.js +146 -0
- package/browser/dist/valbuild-server-browser.cjs.js +7 -0
- package/browser/dist/valbuild-server-browser.cjs.prod.js +146 -0
- package/browser/dist/valbuild-server-browser.esm.js +142 -0
- package/browser/package.json +4 -0
- package/dist/ValRouterCore-42772c21.cjs.prod.js +10824 -0
- package/dist/ValRouterCore-7ca3dc68.esm.js +10784 -0
- package/dist/ValRouterCore-9bdaaf89.cjs.dev.js +10824 -0
- package/dist/declarations/src/ValOps.d.ts +44 -11
- package/dist/declarations/src/ValOpsFS.d.ts +1 -0
- package/dist/declarations/src/ValOpsHttp.d.ts +1 -0
- package/dist/declarations/src/ValOpsMemory.d.ts +13 -4
- package/dist/declarations/src/ValRouter.d.ts +1 -1
- package/dist/declarations/src/ValServer.d.ts +9 -3
- package/dist/declarations/src/browser/inTabFetch.d.ts +27 -0
- package/dist/declarations/src/browser/index.d.ts +9 -0
- package/dist/declarations/src/checkRemoteRef.d.ts +40 -2
- package/dist/declarations/src/createFixPatch.d.ts +25 -3
- package/dist/declarations/src/fixFiles.d.ts +42 -0
- package/dist/declarations/src/fixHandlers.d.ts +15 -1
- package/dist/declarations/src/getSettings.d.ts +1 -0
- package/dist/declarations/src/index.d.ts +1 -0
- package/dist/declarations/src/linkCheck/probeUrl.d.ts +40 -0
- package/dist/declarations/src/platform.d.ts +55 -0
- package/dist/declarations/src/remoteFileCheck.d.ts +116 -0
- package/dist/declarations/src/remoteHostCheck.d.ts +101 -0
- package/dist/declarations/src/studioValidation.d.ts +84 -0
- package/dist/declarations/src/uploadRemoteFile.d.ts +1 -1
- package/dist/declarations/src/videosetFixes.d.ts +3 -2
- package/dist/getJsonEntryAtCommit-2e36e805.cjs.prod.js +142 -0
- package/dist/getJsonEntryAtCommit-40b6d9c1.esm.js +135 -0
- package/dist/getJsonEntryAtCommit-64f67a50.cjs.dev.js +142 -0
- package/dist/jsonValuesModule-427d68c8.esm.js +273 -0
- package/dist/jsonValuesModule-8ae503a8.cjs.dev.js +288 -0
- package/dist/jsonValuesModule-d1bb8d8f.cjs.prod.js +288 -0
- package/dist/ops-0d05feaf.esm.js +353 -0
- package/dist/ops-b15239ac.cjs.prod.js +361 -0
- package/dist/ops-f37d18c6.cjs.dev.js +361 -0
- package/dist/publishKit-580f558a.cjs.prod.js +41 -0
- package/dist/publishKit-5865095c.cjs.dev.js +41 -0
- package/dist/publishKit-bc8df53d.esm.js +31 -0
- package/dist/valbuild-server.cjs.dev.js +9239 -18895
- package/dist/valbuild-server.cjs.prod.js +9239 -18895
- package/dist/valbuild-server.esm.js +9107 -18763
- package/package.json +17 -5
|
@@ -4,7 +4,8 @@ import { JSONValue, ParentRef, Patch, PatchError } from "@valbuild/core/patch";
|
|
|
4
4
|
import type { HistoryError } from "./history/HistoryError.js";
|
|
5
5
|
import type { PlanRefusalErrorT } from "@valbuild/shared/internal";
|
|
6
6
|
import type { AffectedFile, StoredModuleVersion, CommitPage, CommitPatch, HistoricalCommit } from "./history/types.js";
|
|
7
|
-
import {
|
|
7
|
+
import { type RemoteFilesCheck, type RemoteNetwork } from "./remoteFileCheck.js";
|
|
8
|
+
import type { ValSyntaxError, ValSyntaxErrorTree } from "./patch/ts/syntax.js";
|
|
8
9
|
import { ParentPatchId } from "@valbuild/core";
|
|
9
10
|
import type { ReifiedPreview } from "@valbuild/core";
|
|
10
11
|
import { ValCommit, ValDeployment } from "@valbuild/shared/internal";
|
|
@@ -382,7 +383,14 @@ export declare abstract class ValOps {
|
|
|
382
383
|
* are absent. `/sources/~` overlays the two for exactly this reason.
|
|
383
384
|
*/
|
|
384
385
|
getSourcesWithPatchesApplied(analysis: PatchAnalysis & OrderedPatches): Promise<Awaited<ReturnType<ValOps["getSources"]>>>;
|
|
385
|
-
validateSources(schemas: Schemas, sources: Sources, patchesByModule?: PatchAnalysis["patchesByModule"]
|
|
386
|
+
validateSources(schemas: Schemas, sources: Sources, patchesByModule?: PatchAnalysis["patchesByModule"],
|
|
387
|
+
/**
|
|
388
|
+
* Validate only these modules. The snapshot the cross-module checks read
|
|
389
|
+
* (a keyOf's keys, a route, a set's directory) is still built from every
|
|
390
|
+
* module, which is what makes checking one module at a time give the same
|
|
391
|
+
* answer for it as checking them all. Wins over `patchesByModule`.
|
|
392
|
+
*/
|
|
393
|
+
onlyModules?: readonly ModuleFilePath[]): Promise<{
|
|
386
394
|
errors: Record<ModuleFilePath, {
|
|
387
395
|
invalidSource?: {
|
|
388
396
|
message: string;
|
|
@@ -392,7 +400,19 @@ export declare abstract class ValOps {
|
|
|
392
400
|
files: Record<SourcePath, FileSource>;
|
|
393
401
|
remoteFiles: Record<SourcePath, MediaSource>;
|
|
394
402
|
}>;
|
|
395
|
-
|
|
403
|
+
/**
|
|
404
|
+
* The remote values `validateSources` set aside, checked: is each ref
|
|
405
|
+
* sound, and does its host serve it. See `checkRemoteFiles`.
|
|
406
|
+
*
|
|
407
|
+
* `fileLastUpdatedByPatchId` names the refs whose bytes are still in a
|
|
408
|
+
* pending patch, which the host will not have until publish.
|
|
409
|
+
*/
|
|
410
|
+
validateRemoteFiles(schemas: Schemas, sources: Sources, remoteFiles: Record<SourcePath, MediaSource>, fileLastUpdatedByPatchId?: PatchAnalysis["fileLastUpdatedByPatchId"], network?: RemoteNetwork): Promise<RemoteFilesCheck>;
|
|
411
|
+
/**
|
|
412
|
+
* Is a remote file's bytes in a pending patch? `not-pending` when no patch
|
|
413
|
+
* wrote it; `missing` when one did and its bytes are not there.
|
|
414
|
+
*/
|
|
415
|
+
pendingBytesOf(fileLastUpdatedByPatchId: PatchAnalysis["fileLastUpdatedByPatchId"]): (ref: string) => Promise<"present" | "missing" | "not-pending">;
|
|
396
416
|
validateFiles(schemas: Schemas, sources: Sources, files: Record<SourcePath, FileSource>, fileLastUpdatedByPatchId?: PatchAnalysis["fileLastUpdatedByPatchId"]): Promise<Record<SourcePath, ValidationError[]>>;
|
|
397
417
|
/**
|
|
398
418
|
* Applies the pending patches to the source files so they can be committed.
|
|
@@ -598,6 +618,17 @@ export declare abstract class ValOps {
|
|
|
598
618
|
* the ones that create patches and publish.
|
|
599
619
|
*/
|
|
600
620
|
abstract readonly requiresAuth: boolean;
|
|
621
|
+
/**
|
|
622
|
+
* Which implementation this is, for the routes that differ by mode.
|
|
623
|
+
*
|
|
624
|
+
* A tag rather than `instanceof`, because `instanceof ValOpsFS` needs the
|
|
625
|
+
* class itself, and importing the class imports `fs`: `ValServer` asking
|
|
626
|
+
* which mode it is in was what put the filesystem and the content-service
|
|
627
|
+
* client in the module graph of a server that runs in a browser tab (the
|
|
628
|
+
* demo, `@valbuild/server/browser`). `isFsOps` and friends in `opsKind.ts`
|
|
629
|
+
* narrow on it with type-only imports.
|
|
630
|
+
*/
|
|
631
|
+
abstract readonly kind: "fs" | "http" | "memory";
|
|
601
632
|
abstract onInit(baseSha: BaseSha, schemaSha: SchemaSha): Promise<void>;
|
|
602
633
|
abstract fetchPatches<ExcludePatchOps extends boolean>(filters: {
|
|
603
634
|
patchIds?: PatchId[];
|
|
@@ -629,13 +660,13 @@ export declare abstract class ValOps {
|
|
|
629
660
|
* gets this for free; a backend that can take bytes straight through should
|
|
630
661
|
* override it.
|
|
631
662
|
*/
|
|
632
|
-
saveBinaryFileFromPatch(filePath: string, parentRef: ParentRef, patchId: PatchId, bytes:
|
|
663
|
+
saveBinaryFileFromPatch(filePath: string, parentRef: ParentRef, patchId: PatchId, bytes: Uint8Array, mimeType: string, type: "file" | "image", metadata: MetadataOfType<"file" | "image"> | undefined): Promise<WithGenericError<{
|
|
633
664
|
patchId: PatchId;
|
|
634
665
|
filePath: string;
|
|
635
666
|
}>>;
|
|
636
|
-
abstract getBase64EncodedBinaryFileFromPatch(filePath: string, patchId: PatchId, remote: boolean): Promise<
|
|
667
|
+
abstract getBase64EncodedBinaryFileFromPatch(filePath: string, patchId: PatchId, remote: boolean): Promise<Uint8Array | null>;
|
|
637
668
|
protected abstract getBase64EncodedBinaryFileMetadataFromPatch<T extends "file" | "image">(filePath: string, type: T, patchId: PatchId, remote: boolean): Promise<OpsMetadata<T>>;
|
|
638
|
-
abstract getBinaryFile(filePathOrRef: string): Promise<
|
|
669
|
+
abstract getBinaryFile(filePathOrRef: string): Promise<Uint8Array | null>;
|
|
639
670
|
/**
|
|
640
671
|
* A file's bytes as something a `Range` request can be answered from
|
|
641
672
|
* without reading the rest: a video is seeked in by many small ranges, and
|
|
@@ -698,7 +729,7 @@ export declare abstract class ValOps {
|
|
|
698
729
|
/** Which files the commit touched, and how. Names them; does not fetch them. */
|
|
699
730
|
abstract getCommitAffectedFiles(commitSha: string): Promise<result.Result<AffectedFile[], HistoryError>>;
|
|
700
731
|
/** One file's bytes as they were at one commit. */
|
|
701
|
-
abstract getFileAtCommit(commitSha: string, filePath: string, remote: boolean): Promise<result.Result<
|
|
732
|
+
abstract getFileAtCommit(commitSha: string, filePath: string, remote: boolean): Promise<result.Result<Uint8Array, HistoryError>>;
|
|
702
733
|
/**
|
|
703
734
|
* Where a module lives in the REPOSITORY, as `getFileAtCommit` wants it.
|
|
704
735
|
*
|
|
@@ -1007,13 +1038,15 @@ export type OrderedPatchesMetadata = {
|
|
|
1007
1038
|
networkError?: boolean;
|
|
1008
1039
|
};
|
|
1009
1040
|
export declare function getFieldsForType<T extends BinaryFileType>(type: T): (keyof MetadataOfType<T> & string)[];
|
|
1010
|
-
export declare function createMetadataFromBuffer<T extends BinaryFileType>(type: BinaryFileType, mimeType: string, buffer:
|
|
1041
|
+
export declare function createMetadataFromBuffer<T extends BinaryFileType>(type: BinaryFileType, mimeType: string, buffer: Uint8Array): OpsMetadata<T>;
|
|
1011
1042
|
export declare function guessMimeTypeFromPath(filePath: string): string | null;
|
|
1012
|
-
export declare function bufferFromDataUrl(dataUrl: string
|
|
1043
|
+
export declare function bufferFromDataUrl(dataUrl: string,
|
|
1044
|
+
/** Node passes `Buffer`'s, which is faster: this is a whole upload. */
|
|
1045
|
+
decodeBase64?: (base64: string) => Uint8Array): Uint8Array | undefined;
|
|
1013
1046
|
/** A file of known size whose bytes are read a range at a time. */
|
|
1014
1047
|
export type BinaryFileReader = {
|
|
1015
1048
|
readonly size: number;
|
|
1016
1049
|
/** Bytes `start` to `end`, both inclusive, as an HTTP range is. */
|
|
1017
|
-
read(start: number, end: number): Promise<
|
|
1050
|
+
read(start: number, end: number): Promise<Uint8Array>;
|
|
1018
1051
|
};
|
|
1019
|
-
export declare function bufferReader(buffer:
|
|
1052
|
+
export declare function bufferReader(buffer: Uint8Array): BinaryFileReader;
|
|
@@ -12,6 +12,7 @@ export declare class ValOpsFS extends ValOps {
|
|
|
12
12
|
private readonly host;
|
|
13
13
|
/** The developer's own working tree. See {@link ValOps.patchesAreLocal}. */
|
|
14
14
|
readonly patchesAreLocal = true;
|
|
15
|
+
readonly kind = "fs";
|
|
15
16
|
/**
|
|
16
17
|
* The developer's own machine, where there is no credential to require and
|
|
17
18
|
* nothing to protect it from. See {@link ValOps.requiresAuth}.
|
|
@@ -43,6 +43,7 @@ export declare class ValOpsHttp extends ValOps {
|
|
|
43
43
|
private readonly root;
|
|
44
44
|
/** Val's content service owns the store. See {@link ValOps.patchesAreLocal}. */
|
|
45
45
|
readonly patchesAreLocal = false;
|
|
46
|
+
readonly kind = "http";
|
|
46
47
|
/** See {@link ValOps.requiresAuth}. */
|
|
47
48
|
readonly requiresAuth = true;
|
|
48
49
|
/**
|
|
@@ -34,7 +34,7 @@ export type StoredFile = {
|
|
|
34
34
|
* has resolved that before it asks.
|
|
35
35
|
*/
|
|
36
36
|
filePath: string;
|
|
37
|
-
data:
|
|
37
|
+
data: Uint8Array;
|
|
38
38
|
metadata: MetadataOfType<"file" | "image"> | undefined;
|
|
39
39
|
};
|
|
40
40
|
/**
|
|
@@ -121,6 +121,14 @@ export type ValOpsMemoryOptions = ValOpsOptions & {
|
|
|
121
121
|
* startup.
|
|
122
122
|
*/
|
|
123
123
|
unsafelyAllowUnauthenticated?: boolean;
|
|
124
|
+
/**
|
|
125
|
+
* Whether to warn that the server is unauthenticated. On by default, because
|
|
126
|
+
* a deployed server serving without authentication is a server anyone can
|
|
127
|
+
* write to. Off only where nobody else CAN reach it: a server running in a
|
|
128
|
+
* browser tab (`@valbuild/server/browser`), where the warning would be
|
|
129
|
+
* printed into a visitor's console about a port that does not exist.
|
|
130
|
+
*/
|
|
131
|
+
warnUnauthenticated?: boolean;
|
|
124
132
|
/**
|
|
125
133
|
* Val's content host, for pushing remote files at publish.
|
|
126
134
|
*
|
|
@@ -168,6 +176,7 @@ export declare class ValOpsMemory extends ValOps {
|
|
|
168
176
|
* in. Where the two differ is not something a route asks about.
|
|
169
177
|
*/
|
|
170
178
|
readonly patchesAreLocal = true;
|
|
179
|
+
readonly kind = "memory";
|
|
171
180
|
/**
|
|
172
181
|
* Required, unless the host explicitly takes the boundary itself.
|
|
173
182
|
*
|
|
@@ -285,9 +294,9 @@ export declare class ValOpsMemory extends ValOps {
|
|
|
285
294
|
patchId: PatchId;
|
|
286
295
|
filePath: string;
|
|
287
296
|
}>>;
|
|
288
|
-
getBase64EncodedBinaryFileFromPatch(filePath: string, patchId: PatchId, _remote?: boolean): Promise<
|
|
297
|
+
getBase64EncodedBinaryFileFromPatch(filePath: string, patchId: PatchId, _remote?: boolean): Promise<Uint8Array | null>;
|
|
289
298
|
protected getBase64EncodedBinaryFileMetadataFromPatch<T extends "file" | "image">(filePath: string, type: T, patchId: PatchId, _remote?: boolean): Promise<OpsMetadata<T>>;
|
|
290
|
-
getBinaryFile(_filePathOrRef: string): Promise<
|
|
299
|
+
getBinaryFile(_filePathOrRef: string): Promise<Uint8Array | null>;
|
|
291
300
|
protected getBinaryFileMetadata<T extends "file" | "image">(_filePath: string, _type: T): Promise<OpsMetadata<T>>;
|
|
292
301
|
listCommits(): Promise<result.Result<CommitPage, HistoryError>>;
|
|
293
302
|
getCommitPatches(): Promise<result.Result<{
|
|
@@ -299,7 +308,7 @@ export declare class ValOpsMemory extends ValOps {
|
|
|
299
308
|
complete: boolean;
|
|
300
309
|
}, HistoryError>>;
|
|
301
310
|
getCommitAffectedFiles(): Promise<result.Result<AffectedFile[], HistoryError>>;
|
|
302
|
-
getFileAtCommit(): Promise<result.Result<
|
|
311
|
+
getFileAtCommit(): Promise<result.Result<Uint8Array, HistoryError>>;
|
|
303
312
|
gitPathOfModule(_moduleFilePath: ModuleFilePath): result.Result<string, HistoryError>;
|
|
304
313
|
}
|
|
305
314
|
/** Kept exported so a host can name the error shape it may get back. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ValConfig, ValModules } from "@valbuild/core";
|
|
2
|
-
import { ValServerGenericResult } from "@valbuild/shared/internal";
|
|
2
|
+
import type { ValServerGenericResult } from "@valbuild/shared/internal";
|
|
3
3
|
import { ValServer, ValServerCallbacks } from "./ValServer.js";
|
|
4
4
|
type Versions = {
|
|
5
5
|
versions?: {
|
|
@@ -3,7 +3,8 @@ import { ValModules, ValConfig } from "@valbuild/core";
|
|
|
3
3
|
import { Api, ServerOf } from "@valbuild/shared/internal";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { AuthorId, CommitSha, type GenericErrorMessage, type PreparedCommit } from "./ValOps.js";
|
|
6
|
-
import {
|
|
6
|
+
import type { ValPatchStore } from "./ValOpsMemory.js";
|
|
7
|
+
import type { ValServerPlatform } from "./platform.js";
|
|
7
8
|
export type ValServerOptions = {
|
|
8
9
|
route: string;
|
|
9
10
|
valEnableRedirectUrl?: string;
|
|
@@ -169,7 +170,12 @@ export type ValServerConfig = ValServerOptions & ({
|
|
|
169
170
|
config: ValConfig;
|
|
170
171
|
});
|
|
171
172
|
export type ValServer = ServerOf<Api>;
|
|
172
|
-
export declare const ValServer: (valModules: ValModules, options: ValServerConfig, callbacks: ValServerCallbacks
|
|
173
|
+
export declare const ValServer: (valModules: ValModules, options: ValServerConfig, callbacks: ValServerCallbacks,
|
|
174
|
+
/**
|
|
175
|
+
* The Node-only parts, passed in. `createValServer` passes `nodePlatform`;
|
|
176
|
+
* the demo passes the browser one. See {@link ValServerPlatform}.
|
|
177
|
+
*/
|
|
178
|
+
platform: ValServerPlatform) => ServerOf<Api>;
|
|
173
179
|
export type ValServerCallbacks = {
|
|
174
180
|
isEnabled: () => Promise<boolean>;
|
|
175
181
|
onEnable: (success: boolean) => Promise<void>;
|
|
@@ -190,7 +196,7 @@ export declare const ENABLE_COOKIE_VALUE: {
|
|
|
190
196
|
readonly sameSite: "lax";
|
|
191
197
|
};
|
|
192
198
|
};
|
|
193
|
-
export declare function bufferToReadableStream(buffer:
|
|
199
|
+
export declare function bufferToReadableStream(buffer: Uint8Array): ReadableStream<any>;
|
|
194
200
|
export declare function getRedirectUrl(query: {
|
|
195
201
|
redirect_to?: string | undefined;
|
|
196
202
|
}, overrideHost: string | undefined): string | {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ValConfig, type ValModules } from "@valbuild/core";
|
|
2
|
+
import type { ValPatchStore } from "../ValOpsMemory.js";
|
|
3
|
+
/** A `fetch`, as far as the Studio's client calls one. */
|
|
4
|
+
export type InTabFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
5
|
+
/**
|
|
6
|
+
* Val's API, served in this tab: a `fetch` that never touches the network.
|
|
7
|
+
*
|
|
8
|
+
* The whole server -- the router's request parsing, `ValServer`'s routes,
|
|
9
|
+
* memory mode's patch chain -- runs here, exactly as it runs on a server.
|
|
10
|
+
* What is removed is the hop between them, and nothing else, which is why the
|
|
11
|
+
* demo built on it cannot drift from the product: there is no second
|
|
12
|
+
* implementation to drift. See `docs/plans/demo-mode.md`.
|
|
13
|
+
*
|
|
14
|
+
* Memory mode with no source files, because nothing here publishes (only a
|
|
15
|
+
* publish reads `.val.ts` text), and without authentication, because the only
|
|
16
|
+
* caller is the page this runs in.
|
|
17
|
+
*/
|
|
18
|
+
export declare function createInTabValFetch(valModules: ValModules, options: {
|
|
19
|
+
config: ValConfig;
|
|
20
|
+
/**
|
|
21
|
+
* Where edits live. Memory by default, which is what the demo wants: the
|
|
22
|
+
* edits last as long as the tab's page does, and a reload starts over.
|
|
23
|
+
*/
|
|
24
|
+
patchStore?: ValPatchStore;
|
|
25
|
+
/** The path the Studio's client calls. */
|
|
26
|
+
route?: string;
|
|
27
|
+
}): InTabFetch;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@valbuild/server/browser`: Val's server, for a browser tab.
|
|
3
|
+
*
|
|
4
|
+
* Nothing reachable from here may import a Node builtin --
|
|
5
|
+
* `noNodeFromBrowser.test.ts` walks the graph and fails if one appears. The
|
|
6
|
+
* Node-only parts of the server reach it through `ValServerPlatform`, and this
|
|
7
|
+
* entry passes the browser's.
|
|
8
|
+
*/
|
|
9
|
+
export { createInTabValFetch, type InTabFetch } from "./inTabFetch.js";
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { FileMetadata, ImageMetadata, SerializedFileSchema, SerializedImageSchema } from "@valbuild/core";
|
|
2
|
-
export declare function checkRemoteRef(
|
|
2
|
+
export declare function checkRemoteRef(
|
|
3
|
+
/**
|
|
4
|
+
* Unused: a re-derived ref keeps the host it already names (see below).
|
|
5
|
+
* Kept so the exported signature does not change.
|
|
6
|
+
*/
|
|
7
|
+
_remoteHost: string, ref: string, projectRoot: string, schema: SerializedImageSchema | SerializedFileSchema, metadata: Record<string, unknown> | undefined,
|
|
8
|
+
/**
|
|
9
|
+
* Where the bytes come from when the ref has to be re-derived. Defaults to
|
|
10
|
+
* a download cached under `projectRoot`; the Studio passes one that keeps
|
|
11
|
+
* nothing on disk (`FixFiles.readRemoteFile`).
|
|
12
|
+
*/
|
|
13
|
+
readRemote?: ReadRemoteFile): Promise<{
|
|
3
14
|
status: "success";
|
|
4
15
|
} | {
|
|
5
16
|
status: "fix-required";
|
|
@@ -11,10 +22,37 @@ export declare function checkRemoteRef(remoteHost: string, ref: string, projectR
|
|
|
11
22
|
}>;
|
|
12
23
|
export declare function getCachedRemoteFileDir(projectRoot: string): string;
|
|
13
24
|
export declare function getCachedRemoteFilePath(fileExt: string, currentFileHash: string, remoteFileCacheDir: string): string;
|
|
14
|
-
|
|
25
|
+
/**
|
|
26
|
+
* A remote file's bytes, and the hash they actually have — which is the one a
|
|
27
|
+
* re-derived ref is made from, whatever hash the old ref claimed.
|
|
28
|
+
*/
|
|
29
|
+
export type ReadRemoteFile = (ref: string, fileExt: string, expectedFileHash: string) => Promise<{
|
|
30
|
+
status: "success";
|
|
31
|
+
fileBuffer: Buffer;
|
|
32
|
+
fileHash: string;
|
|
33
|
+
/** Where a download was kept, when it was kept: for error messages. */
|
|
34
|
+
cachedFilePath?: string;
|
|
35
|
+
} | {
|
|
36
|
+
status: "error";
|
|
37
|
+
error: string;
|
|
38
|
+
}>;
|
|
39
|
+
export declare function getFileBufferFromRemote(ref: string, fileExt: string, currentFileHash: string, projectRoot: string): ReturnType<ReadRemoteFile>;
|
|
40
|
+
/**
|
|
41
|
+
* Download a remote file to `filePath`, from where its ref says -- and, for a
|
|
42
|
+
* platform ref whose host fails, from the same file on the project's own
|
|
43
|
+
* `valstart.dev` address, which always serves it (`fallbackRefOf`). The bytes
|
|
44
|
+
* from the fallback are checked against the ref's file hash before they are
|
|
45
|
+
* kept: a different host answering is only safe because of that check.
|
|
46
|
+
*
|
|
47
|
+
* Rejects with the FIRST host's error when both fail: that is the one the ref
|
|
48
|
+
* names, and the one a person can recognise.
|
|
49
|
+
*/
|
|
50
|
+
export declare function downloadFileFromRemote(ref: string, filePath: string): ReturnType<typeof downloadFromUrl>;
|
|
51
|
+
declare function downloadFromUrl(ref: string, filePath: string): Promise<{
|
|
15
52
|
status: "success";
|
|
16
53
|
filePath: string;
|
|
17
54
|
} | {
|
|
18
55
|
status: "error";
|
|
19
56
|
error: string;
|
|
20
57
|
}>;
|
|
58
|
+
export {};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { FileMetadata, ImageMetadata, VideoMetadata, SerializedSchema, Source, SourcePath, ValidationError } from "@valbuild/core";
|
|
2
2
|
import { JSONValue, Patch } from "@valbuild/core/patch";
|
|
3
|
+
import { type AskHostProject } from "./remoteHostCheck.js";
|
|
4
|
+
import { FixFiles } from "./fixFiles.js";
|
|
3
5
|
export type FixPatchRemainingError = ValidationError & {
|
|
4
6
|
sourcePath?: SourcePath;
|
|
5
7
|
};
|
|
@@ -17,6 +19,16 @@ export declare function mediaValue(path: string, metadata: unknown): Record<stri
|
|
|
17
19
|
export declare function createFixPatch(config: {
|
|
18
20
|
projectRoot: string;
|
|
19
21
|
remoteHost: string;
|
|
22
|
+
/**
|
|
23
|
+
* Where a fix reads a file's bytes and puts a downloaded one. Defaults to
|
|
24
|
+
* the disk under `projectRoot`; see {@link FixFiles}.
|
|
25
|
+
*/
|
|
26
|
+
files?: FixFiles;
|
|
27
|
+
/**
|
|
28
|
+
* How a platform ref's host is asked which project it serves. Defaults
|
|
29
|
+
* to `GET <host>/_val/project`; tests replace it.
|
|
30
|
+
*/
|
|
31
|
+
askHostProject?: AskHostProject;
|
|
20
32
|
}, apply: boolean, sourcePath: SourcePath, validationError: ValidationError, remoteFiles: {
|
|
21
33
|
[sourcePath: SourcePath]: {
|
|
22
34
|
ref: string;
|
|
@@ -27,6 +39,16 @@ export declare function createFixPatch(config: {
|
|
|
27
39
|
patch: Patch;
|
|
28
40
|
remainingErrors: FixPatchRemainingError[];
|
|
29
41
|
} | undefined>;
|
|
30
|
-
export declare function getVideoMetadata(
|
|
31
|
-
export declare function getImageMetadata(
|
|
32
|
-
export declare function getFileMetadata(
|
|
42
|
+
export declare function getVideoMetadata(files: FixFiles, fileRef: string): Promise<VideoMetadata>;
|
|
43
|
+
export declare function getImageMetadata(files: FixFiles, validationError: ValidationError): Promise<ImageMetadata>;
|
|
44
|
+
export declare function getFileMetadata(files: FixFiles, validationError: ValidationError): Promise<FileMetadata>;
|
|
45
|
+
/**
|
|
46
|
+
* What an image or file directory's entry records that its bytes disagree
|
|
47
|
+
* with, as `[field, recorded, actual]`. The one comparison: the CLI's
|
|
48
|
+
* `*:check-all-files` fix and the Studio's full check both ask it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function mediaDirEntryMismatches(type: "images" | "files", stored: Record<string, unknown>, actual: {
|
|
51
|
+
width?: number;
|
|
52
|
+
height?: number;
|
|
53
|
+
mimeType?: string;
|
|
54
|
+
}): [field: string, recorded: unknown, actual: unknown][];
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { VideoMetadata } from "@valbuild/core";
|
|
2
|
+
import { type ReadRemoteFile } from "./checkRemoteRef.js";
|
|
3
|
+
/**
|
|
4
|
+
* Where `createFixPatch` reads a project file's bytes, and where it puts a
|
|
5
|
+
* remote file it was asked to download.
|
|
6
|
+
*
|
|
7
|
+
* Every path is the file's path from the project root, the way a source names
|
|
8
|
+
* it: `/public/val/logo.png`, never an absolute path on some disk. That is the
|
|
9
|
+
* point: a project in http mode, or one the platform serves from a Worker, has
|
|
10
|
+
* no disk to join a path onto, and a fix that reads a file has to work there
|
|
11
|
+
* too. `diskFixFiles` is the one the CLI and the language server use, and does
|
|
12
|
+
* exactly what `createFixPatch` did with `fs` before this existed.
|
|
13
|
+
*
|
|
14
|
+
* Not yet covered, and still read from `projectRoot` directly: the CLI's
|
|
15
|
+
* listing of a media directory for files it does not track, and
|
|
16
|
+
* `videos:check-all-files` (`videosetCheckAllFilesPatch`), which lists one.
|
|
17
|
+
* See `docs/plans/studio-validate.md`.
|
|
18
|
+
*/
|
|
19
|
+
export type FixFiles = {
|
|
20
|
+
/** A project file's bytes. Rejects when it cannot be read. */
|
|
21
|
+
readFile(filePath: string): Promise<Buffer>;
|
|
22
|
+
/**
|
|
23
|
+
* A project video's size, length and mime type. Its own method rather than
|
|
24
|
+
* `readFile` because a video is mostly frames, and the disk reader reads
|
|
25
|
+
* only the headers.
|
|
26
|
+
*/
|
|
27
|
+
readVideoMetadata(filePath: string): Promise<VideoMetadata>;
|
|
28
|
+
/**
|
|
29
|
+
* A remote file's bytes, for re-deriving a stale ref (`*:check-remote`).
|
|
30
|
+
* Nothing is stored in the project.
|
|
31
|
+
*/
|
|
32
|
+
readRemoteFile: ReadRemoteFile;
|
|
33
|
+
/** Downloads `url` and stores it in the project at `filePath`. */
|
|
34
|
+
saveRemoteFile(url: string, filePath: string): Promise<{
|
|
35
|
+
status: "success";
|
|
36
|
+
} | {
|
|
37
|
+
status: "error";
|
|
38
|
+
error: string;
|
|
39
|
+
}>;
|
|
40
|
+
};
|
|
41
|
+
/** {@link FixFiles} over a project on disk. */
|
|
42
|
+
export declare function diskFixFiles(projectRoot: string): FixFiles;
|
|
@@ -44,6 +44,8 @@ export type IValRemote = {
|
|
|
44
44
|
remoteFileBuckets: {
|
|
45
45
|
bucket: string;
|
|
46
46
|
}[];
|
|
47
|
+
/** See `getSettings`: the host new refs name. */
|
|
48
|
+
remoteHost?: string;
|
|
47
49
|
};
|
|
48
50
|
} | {
|
|
49
51
|
success: false;
|
|
@@ -134,12 +136,16 @@ export type ValidationEvent = {
|
|
|
134
136
|
sourcePath: string;
|
|
135
137
|
message: string;
|
|
136
138
|
keyError?: boolean;
|
|
139
|
+
/** See `isRepairedOnSave`: the Studio would not hold a publish for it. */
|
|
140
|
+
repairedOnSave?: boolean;
|
|
137
141
|
} | {
|
|
138
142
|
type: "validation-fixable-error";
|
|
139
143
|
sourcePath: string;
|
|
140
144
|
message: string;
|
|
141
145
|
fixable: boolean;
|
|
142
146
|
keyError?: boolean;
|
|
147
|
+
/** See `isRepairedOnSave`: the Studio would not hold a publish for it. */
|
|
148
|
+
repairedOnSave?: boolean;
|
|
143
149
|
} | {
|
|
144
150
|
type: "unknown-fix";
|
|
145
151
|
sourcePath: string;
|
|
@@ -246,7 +252,15 @@ export declare function handleJsonValuesExtractEntry(ctx: FixHandlerContext): Pr
|
|
|
246
252
|
* exists to send it there, and to say what `--fix` would do when it is off.
|
|
247
253
|
*/
|
|
248
254
|
export declare function handleViewCheckModule(ctx: FixHandlerContext): Promise<FixHandlerResult>;
|
|
255
|
+
/**
|
|
256
|
+
* `remote:rewrite-host`: a platform ref whose host no longer serves its
|
|
257
|
+
* project. The server's full check reports it, and so does `createFixPatch`'s
|
|
258
|
+
* `image:check-remote` branch, which is how `val validate` finds it: that
|
|
259
|
+
* branch asks the host first. Either way the patch is `createFixPatch`'s, and
|
|
260
|
+
* needs nothing but the ref -- the new host is derived from it.
|
|
261
|
+
*/
|
|
262
|
+
export declare function handleRemoteRewriteHost(ctx: FixHandlerContext): Promise<FixHandlerResult>;
|
|
249
263
|
export declare function handleExternalUpload(): Promise<FixHandlerResult>;
|
|
250
|
-
export declare const currentFixHandlers: Record<Exclude<ValidationFix, "keyof:check-keys" | "image:check-gallery" | "file:check-gallery" | "video:check-gallery" | "router:check-route" | "locale:check-locale" | "record:fill-keys">, FixHandler>;
|
|
264
|
+
export declare const currentFixHandlers: Record<Exclude<ValidationFix, "keyof:check-keys" | "image:check-gallery" | "file:check-gallery" | "video:check-gallery" | "router:check-route" | "locale:check-locale" | "router:check-locale-param" | "record:fill-keys">, FixHandler>;
|
|
251
265
|
export declare const fixHandlers: Record<string, FixHandler>;
|
|
252
266
|
export declare function createDefaultValFSHost(): IValFSHost;
|
|
@@ -4,6 +4,7 @@ declare const SettingsSchema: z.ZodObject<{
|
|
|
4
4
|
remoteFileBuckets: z.ZodArray<z.ZodObject<{
|
|
5
5
|
bucket: z.ZodString;
|
|
6
6
|
}, z.core.$strip>>;
|
|
7
|
+
remoteHost: z.ZodOptional<z.ZodString>;
|
|
7
8
|
}, z.core.$strip>;
|
|
8
9
|
type Settings = z.infer<typeof SettingsSchema>;
|
|
9
10
|
export declare function getSettings(projectName: string, auth: {
|
|
@@ -19,6 +19,7 @@ export { formatSyntaxErrorTree } from "./patch/ts/syntax.js";
|
|
|
19
19
|
export { analyzeValModule } from "./patch/ts/valModule.js";
|
|
20
20
|
export type { ValModuleAnalysis } from "./patch/ts/valModule.js";
|
|
21
21
|
export { createFixPatch } from "./createFixPatch.js";
|
|
22
|
+
export { askOncePerHost } from "./remoteHostCheck.js";
|
|
22
23
|
export { fixHandlers, currentFixHandlers, createDefaultValFSHost, handleFileMetadata, handleRemoteFileUpload, handleRemoteGalleryFileUpload, handleRemoteFileDownload, handleRemoteFileCheck, handleUniqueFolderCheck, handleCheckAllFiles, handleJsonValuesExtractEntry, handleExternalUpload, } from "./fixHandlers.js";
|
|
23
24
|
export { handleVideosetMetadata, handleVideosetUploadRemote, handleVideosetCheckRemote, handleVideosetCheckAllFiles, videosetEntryVideoSchema, filesOfVideosetEntry, } from "./videosetFixes.js";
|
|
24
25
|
export type { FixHandler, ModulePatch, FixHandlerContext, FixHandlerResult, IValRemote, ValidationEvent, ValidationError, ValModule, } from "./fixHandlers.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import https from "node:https";
|
|
2
|
+
import { type LookupFunction } from "node:net";
|
|
3
|
+
/**
|
|
4
|
+
* Opening one external URL and reporting what answered.
|
|
5
|
+
*
|
|
6
|
+
* Mirrors `ExternalUrlProbeResult` in the Studio - the two are checked against
|
|
7
|
+
* each other by the API route's zod schema, which is the only place they meet.
|
|
8
|
+
*/
|
|
9
|
+
export type ProbeResult = {
|
|
10
|
+
kind: "answered";
|
|
11
|
+
code: number;
|
|
12
|
+
finalUrl: string;
|
|
13
|
+
ms: number;
|
|
14
|
+
} | {
|
|
15
|
+
kind: "unreachable";
|
|
16
|
+
message: string;
|
|
17
|
+
} | {
|
|
18
|
+
kind: "timeout";
|
|
19
|
+
ms: number;
|
|
20
|
+
} | {
|
|
21
|
+
kind: "skipped";
|
|
22
|
+
message: string;
|
|
23
|
+
};
|
|
24
|
+
export type ProbeOptions = {
|
|
25
|
+
/** The whole check, redirects included. */
|
|
26
|
+
timeoutMs?: number;
|
|
27
|
+
/** How many redirects to follow before calling it a loop. */
|
|
28
|
+
maxRedirects?: number;
|
|
29
|
+
/**
|
|
30
|
+
* The transport, injected so the redirect and status logic can be tested
|
|
31
|
+
* without a network. Production passes nothing and gets `node:https`.
|
|
32
|
+
*/
|
|
33
|
+
request?: typeof https.request;
|
|
34
|
+
/** Injected for the same reason, and never to relax the address guard. */
|
|
35
|
+
now?: () => number;
|
|
36
|
+
};
|
|
37
|
+
export declare const DEFAULT_TIMEOUT_MS = 5000;
|
|
38
|
+
export declare const DEFAULT_MAX_REDIRECTS = 5;
|
|
39
|
+
export declare function guardedLookup(lookup: LookupFunction): LookupFunction;
|
|
40
|
+
export declare function probeUrl(rawUrl: string, options?: ProbeOptions): Promise<ProbeResult>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ValModules } from "@valbuild/core";
|
|
2
|
+
import type { ValOpsFS } from "./ValOpsFS.js";
|
|
3
|
+
import type { ValOpsHttp } from "./ValOpsHttp.js";
|
|
4
|
+
import type { ValOpsMemory } from "./ValOpsMemory.js";
|
|
5
|
+
import type { ValServerConfig } from "./ValServer.js";
|
|
6
|
+
import type { ResolveRemoteFileAuthResult } from "./valServerConfig.js";
|
|
7
|
+
import type { JwtResult } from "./jwt.js";
|
|
8
|
+
import type { ProbeResult } from "./linkCheck/probeUrl.js";
|
|
9
|
+
import type { checkModuleForStudio, fixForStudio } from "./studioValidation.js";
|
|
10
|
+
/**
|
|
11
|
+
* What `ValServer` needs from the machine it runs on.
|
|
12
|
+
*
|
|
13
|
+
* Everything else in `ValServer` is request handling, and runs anywhere. These
|
|
14
|
+
* are the parts that reach for Node -- a filesystem, a socket, a keyed hash --
|
|
15
|
+
* and they are passed in rather than imported so that the same server can run
|
|
16
|
+
* in a browser tab: the demo (`@valbuild/server/browser`) serves a memory-mode
|
|
17
|
+
* server to the Studio with no network in between.
|
|
18
|
+
*
|
|
19
|
+
* `nodePlatform` is the real one, and `createValServer` passes it. A function
|
|
20
|
+
* here is whatever an implementation can honestly answer: the browser one has
|
|
21
|
+
* no filesystem to read a token from, so it answers as a server without one
|
|
22
|
+
* does.
|
|
23
|
+
*/
|
|
24
|
+
export type ValServerPlatform = {
|
|
25
|
+
/** Builds the data layer the config asks for. See `createValOps`. */
|
|
26
|
+
createOps: (valModules: ValModules, options: ValServerConfig) => ValOpsFS | ValOpsHttp | ValOpsMemory;
|
|
27
|
+
/** Whether an external URL answers. See `linkCheck/probeUrl`. */
|
|
28
|
+
probeUrl: (url: string) => Promise<ProbeResult>;
|
|
29
|
+
/** The session cookie's signature. See `jwt.ts`. */
|
|
30
|
+
jwt: {
|
|
31
|
+
verifyJwt: (token: string, secretKey: string) => JwtResult;
|
|
32
|
+
decodeJwtWithoutVerifying: (token: string) => JwtResult;
|
|
33
|
+
encodeJwt: (payload: object, sessionKey: string) => string;
|
|
34
|
+
getExpire: () => number;
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* `bytes` as base64. For the paths that encode every file a publish commits:
|
|
38
|
+
* Node's `Buffer` is ten times faster than `btoa`, and a browser has only
|
|
39
|
+
* `btoa` (`bytesToBase64` in `bytes.ts`). Same answer either way.
|
|
40
|
+
*/
|
|
41
|
+
bytesToBase64: (bytes: Uint8Array) => string;
|
|
42
|
+
/** The developer's `val login` token. See `readValLoginToken`. */
|
|
43
|
+
readValLoginToken: (options: ValServerConfig) => Promise<string | null>;
|
|
44
|
+
/** Which credential talks to the content host. See `resolveRemoteFileAuth`. */
|
|
45
|
+
resolveRemoteFileAuth: (options: ValServerConfig) => Promise<ResolveRemoteFileAuthResult>;
|
|
46
|
+
/**
|
|
47
|
+
* The Studio's full check (`/validate`, `/validate/fix`). It reads files'
|
|
48
|
+
* bytes and asks remote hosts, so it needs Node; absent in a browser, where
|
|
49
|
+
* both routes answer that it is not available.
|
|
50
|
+
*/
|
|
51
|
+
studioValidation?: {
|
|
52
|
+
checkModuleForStudio: typeof checkModuleForStudio;
|
|
53
|
+
fixForStudio: typeof fixForStudio;
|
|
54
|
+
};
|
|
55
|
+
};
|