@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
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { MediaSource, SerializedSchema, SourcePath, ValidationError } from "@valbuild/core";
|
|
2
|
+
import { type AskHostProject } from "./remoteHostCheck.js";
|
|
3
|
+
/**
|
|
4
|
+
* Does the host a remote ref names still serve it?
|
|
5
|
+
*
|
|
6
|
+
* `found` and `missing` are the host's answer. `unreachable` is no answer at
|
|
7
|
+
* all — a timeout, a refused connection, a 5xx — and says nothing about the
|
|
8
|
+
* file, only about the host.
|
|
9
|
+
*/
|
|
10
|
+
export type RemoteFileAnswer = {
|
|
11
|
+
status: "found";
|
|
12
|
+
} | {
|
|
13
|
+
status: "missing";
|
|
14
|
+
httpStatus: number;
|
|
15
|
+
} | {
|
|
16
|
+
status: "unreachable";
|
|
17
|
+
reason: string;
|
|
18
|
+
};
|
|
19
|
+
export type HeadRemoteFile = (url: string) => Promise<RemoteFileAnswer>;
|
|
20
|
+
/**
|
|
21
|
+
* How the remote check reaches the network: what it asks a host about a file,
|
|
22
|
+
* and which project a platform host serves. Both default to the real thing;
|
|
23
|
+
* tests replace them.
|
|
24
|
+
*/
|
|
25
|
+
export type RemoteNetwork = {
|
|
26
|
+
head?: HeadRemoteFile;
|
|
27
|
+
askHostProject?: AskHostProject;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* A HEAD, falling back to the first byte of a GET for a host that does not
|
|
31
|
+
* answer HEAD (405, 501): either way no file is downloaded to learn that it is
|
|
32
|
+
* there.
|
|
33
|
+
*/
|
|
34
|
+
export declare const headRemoteFile: HeadRemoteFile;
|
|
35
|
+
/** For tests: forget every answer. */
|
|
36
|
+
export declare function clearRemoteFileAnswers(): void;
|
|
37
|
+
/** A host that did not serve the refs of a module it was asked about. */
|
|
38
|
+
export type RemoteHostProblem = {
|
|
39
|
+
/** `https://remote.val.build`, as the ref names it. */
|
|
40
|
+
host: string;
|
|
41
|
+
status: "missing" | "unreachable";
|
|
42
|
+
/** What the host answered, for "Details": never the row's sentence. */
|
|
43
|
+
details: string;
|
|
44
|
+
/** Every ref on that host in the module, checked or vouched for. */
|
|
45
|
+
sourcePaths: SourcePath[];
|
|
46
|
+
};
|
|
47
|
+
export type RemoteFilesCheck = {
|
|
48
|
+
/** Wrong refs, one by one: each has its own row. */
|
|
49
|
+
errors: Record<SourcePath, ValidationError[]>;
|
|
50
|
+
/** Hosts that failed: one row each, however many refs they cover. */
|
|
51
|
+
hosts: RemoteHostProblem[];
|
|
52
|
+
};
|
|
53
|
+
export type RemoteFilesCheckInput = {
|
|
54
|
+
/** What `validateSources` set aside as remote: every remote media value. */
|
|
55
|
+
remoteFiles: Record<SourcePath, MediaSource>;
|
|
56
|
+
/** The serialized schema at a path, or undefined when it does not resolve. */
|
|
57
|
+
schemaAt: (sourcePath: SourcePath) => SerializedSchema | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* Does this ref have its bytes in a pending patch? They go to the remote
|
|
60
|
+
* host at publish, so until then asking the host for it is asking a
|
|
61
|
+
* question whose answer is "not yet".
|
|
62
|
+
*/
|
|
63
|
+
pendingBytes: (ref: string) => Promise<"present" | "missing" | "not-pending">;
|
|
64
|
+
/**
|
|
65
|
+
* Is this value's ref the one already published at this path? Only those
|
|
66
|
+
* may be vouched for by another ref on the same host; see `sampleByHost`.
|
|
67
|
+
*/
|
|
68
|
+
isPublished: (sourcePath: SourcePath, ref: string) => boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Ask one published ref per host and take its answer for the rest.
|
|
71
|
+
*
|
|
72
|
+
* Right for a managed project, whose refs are only ever written by the
|
|
73
|
+
* Studio and whose host changes for every ref at once. Wrong for a connected
|
|
74
|
+
* one, where a developer can edit any single ref by hand. See
|
|
75
|
+
* `docs/plans/studio-validate.md`, "Remote refs in the full check".
|
|
76
|
+
*/
|
|
77
|
+
sampleByHost: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Ask only whether the host serves each ref. For the keys of a media
|
|
80
|
+
* directory: what is recorded about the file is the entry, not a value at
|
|
81
|
+
* the path, and the ref's hash was made from a schema the directory
|
|
82
|
+
* synthesizes for it, so there is no schema here to judge it by.
|
|
83
|
+
*/
|
|
84
|
+
existenceOnly?: boolean;
|
|
85
|
+
head?: HeadRemoteFile;
|
|
86
|
+
/** How a platform ref's host is asked which project it serves. */
|
|
87
|
+
askHostProject?: AskHostProject;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* The remote half of the Studio's full check, for one module's remote values.
|
|
91
|
+
*
|
|
92
|
+
* Four questions, cheapest first, and a ref stops at the first one it fails:
|
|
93
|
+
*
|
|
94
|
+
* 1. **Is it a ref at all?** `splitRemoteRef` parses it.
|
|
95
|
+
* 2. **Does the ref agree with what is recorded about it?** A ref carries a
|
|
96
|
+
* hash of the schema's options, the file's extension and its width, height
|
|
97
|
+
* and mime type. When the value or the schema changed and the ref did not,
|
|
98
|
+
* the ref is stale, and **Fix** (`image:check-remote`) re-reads the file and
|
|
99
|
+
* writes the ref it should have. Computed with the ref's OWN core version:
|
|
100
|
+
* the CLI also re-derives every ref after a Val upgrade, and listing every
|
|
101
|
+
* remote image in the project after each upgrade would bury the rows that
|
|
102
|
+
* matter. Nothing here downloads.
|
|
103
|
+
* 3. **Does its host still serve its project?** Only for a platform ref
|
|
104
|
+
* (`…/_val/file/…`) that is not on the project's own `valstart.dev`
|
|
105
|
+
* address: one `GET <host>/_val/project` per host. A host that dropped the
|
|
106
|
+
* project gets a **Fix** (`remote:rewrite-host`) that points the ref at
|
|
107
|
+
* `<public id>.valstart.dev`. See `remoteHostCheck.ts`.
|
|
108
|
+
* 4. **Does the host have it?** A HEAD.
|
|
109
|
+
*
|
|
110
|
+
* Neither host question is asked of a ref whose bytes are in a pending patch,
|
|
111
|
+
* which the host will not have until publish: there the question is whether
|
|
112
|
+
* the patch still has them.
|
|
113
|
+
*/
|
|
114
|
+
export declare function checkRemoteFiles(input: RemoteFilesCheckInput): Promise<RemoteFilesCheck>;
|
|
115
|
+
/** `www.acme.no`, from `https://www.acme.no`: how a person names a host. */
|
|
116
|
+
export declare function hostName(host: string): string;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does the host a platform ref names still serve the project the ref names?
|
|
3
|
+
*
|
|
4
|
+
* A ref served by the platform loader names the project's host at the time
|
|
5
|
+
* the file was added: `https://www.acme.no/_val/file/p/3f9a2c1/…`. When the
|
|
6
|
+
* project drops `www.acme.no`, the bytes are still there, at
|
|
7
|
+
* `3f9a2c1.valstart.dev`, but every ref that names the old domain points at a
|
|
8
|
+
* host that no longer serves them. This is the question that finds those refs,
|
|
9
|
+
* and `rewriteRefHost` is what the fix (`remote:rewrite-host`) does about it.
|
|
10
|
+
* See valbuild/home `docs/remote-files-on-platform.md`, "Rewriting refs after a
|
|
11
|
+
* domain is removed", and `docs/plans/studio-validate.md`, step 6.
|
|
12
|
+
*
|
|
13
|
+
* Asked of the host itself (`GET <host>/_val/project`), not of content: the
|
|
14
|
+
* host's answer covers DNS, the certificate and the loader's mapping in one,
|
|
15
|
+
* it needs no credential, and it works for a ref that names a project other
|
|
16
|
+
* than this one -- a project seeded from a template has refs naming the
|
|
17
|
+
* template's.
|
|
18
|
+
*/
|
|
19
|
+
/** The domain every platform project is always served at, by its public id. */
|
|
20
|
+
export declare const PLATFORM_SITES_DOMAIN = "valstart.dev";
|
|
21
|
+
/**
|
|
22
|
+
* The host every platform ref of `publicProjectId` can always be pointed at:
|
|
23
|
+
* `<public id>.valstart.dev` is permanent (decision 4 of the remote-files
|
|
24
|
+
* plan), and the loader serves a project's files there whatever else it has.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fallbackRemoteHost(publicProjectId: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Only a platform ref is asked about. `remote.val.build` is not a platform
|
|
29
|
+
* host and is not going away, and a ref already on the project's own
|
|
30
|
+
* `valstart.dev` address is on the fallback, which always serves it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function needsHostCheck(remoteHost: string, projectId: string): boolean;
|
|
33
|
+
/** `ref` with its host part replaced, and everything else as it was. */
|
|
34
|
+
export declare function rewriteRefHost(ref: string, remoteHost: string): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* - `serves`: the host answered for the project the ref names.
|
|
37
|
+
* - `other-project`: it answered, for another one -- the domain was moved to
|
|
38
|
+
* a different project, and decision 5 means it will not serve these files.
|
|
39
|
+
* - `no-site`: it answered, but serves no project at all (the loader's 404 for
|
|
40
|
+
* a domain nobody has mapped any more).
|
|
41
|
+
* - `unreachable`: no answer. A domain whose DNS was removed looks like this,
|
|
42
|
+
* and so does a host that is down for a minute, which is why the row says
|
|
43
|
+
* which it saw.
|
|
44
|
+
*/
|
|
45
|
+
export type HostServes = {
|
|
46
|
+
status: "serves";
|
|
47
|
+
} | {
|
|
48
|
+
status: "other-project";
|
|
49
|
+
publicProjectId: string;
|
|
50
|
+
} | {
|
|
51
|
+
status: "no-site";
|
|
52
|
+
httpStatus: number;
|
|
53
|
+
} | {
|
|
54
|
+
status: "unreachable";
|
|
55
|
+
reason: string;
|
|
56
|
+
};
|
|
57
|
+
export type AskHostProject = (remoteHost: string) => Promise<{
|
|
58
|
+
status: "answered";
|
|
59
|
+
publicProjectId: string;
|
|
60
|
+
} | {
|
|
61
|
+
status: "no-site";
|
|
62
|
+
httpStatus: number;
|
|
63
|
+
} | {
|
|
64
|
+
status: "unreachable";
|
|
65
|
+
reason: string;
|
|
66
|
+
}>;
|
|
67
|
+
/** `GET <host>/_val/project`, answered by the platform loader. */
|
|
68
|
+
export declare const askHostProject: AskHostProject;
|
|
69
|
+
export declare function clearHostAnswers(): void;
|
|
70
|
+
export declare function hostServes(remoteHost: string, projectId: string, ask?: AskHostProject): Promise<HostServes>;
|
|
71
|
+
/**
|
|
72
|
+
* `ask`, asking each host once for as long as the returned function is kept:
|
|
73
|
+
* one check, not the life of the server. A check walks its refs in order, so
|
|
74
|
+
* without it every ref on a host that does not answer waits out its own
|
|
75
|
+
* timeout -- 150 images on a dead domain is 20 minutes. Kept per check rather
|
|
76
|
+
* than in {@link hostServes}'s cache because a host that did not answer has
|
|
77
|
+
* to be asked again by the next one.
|
|
78
|
+
*/
|
|
79
|
+
export declare function askOncePerHost(ask?: AskHostProject): AskHostProject;
|
|
80
|
+
/** `www.acme.no`, from `https://www.acme.no/_val`: how a person names a host. */
|
|
81
|
+
export declare function hostNameOfRemoteHost(remoteHost: string): string;
|
|
82
|
+
/**
|
|
83
|
+
* Where a platform ref has to move to, when its host no longer serves the
|
|
84
|
+
* project it names; `undefined` when it does not have to (or is not a
|
|
85
|
+
* platform ref). `why` and `to` are for the caller's sentence: what can be
|
|
86
|
+
* done about it differs by caller (a Studio **Fix**, `val validate --fix`, or
|
|
87
|
+
* nothing yet).
|
|
88
|
+
*/
|
|
89
|
+
export declare function movedRemoteHost(ref: string, ask?: AskHostProject): Promise<{
|
|
90
|
+
ref: string;
|
|
91
|
+
why: string;
|
|
92
|
+
to: string;
|
|
93
|
+
} | undefined>;
|
|
94
|
+
/**
|
|
95
|
+
* Where else a ref's file can be downloaded from: the same file on the
|
|
96
|
+
* project's own `valstart.dev` address, for a platform ref on any other host.
|
|
97
|
+
* `null` when there is nowhere else -- a `remote.val.build` ref, or one already
|
|
98
|
+
* on the fallback. See valbuild/home `docs/remote-files-on-platform.md`,
|
|
99
|
+
* "Tooling: the fallback".
|
|
100
|
+
*/
|
|
101
|
+
export declare function fallbackRefOf(ref: string): string | null;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ModuleFilePath, SourcePath, ValidationError, ValidationFix } from "@valbuild/core";
|
|
2
|
+
import type { Patch } from "@valbuild/core/patch";
|
|
3
|
+
import { FixPatchRemainingError } from "./createFixPatch.js";
|
|
4
|
+
import type { FixFiles } from "./fixFiles.js";
|
|
5
|
+
import { type RemoteHostProblem, type RemoteNetwork } from "./remoteFileCheck.js";
|
|
6
|
+
import { type PatchAnalysis, type Schemas, type Sources, type ValOps } from "./ValOps.js";
|
|
7
|
+
/**
|
|
8
|
+
* The Studio's full check, one module at a time: what `/validate` and
|
|
9
|
+
* `/validate/fix` answer with. See `docs/plans/studio-validate.md`.
|
|
10
|
+
*
|
|
11
|
+
* Both take the sources the caller is looking at — base plus the patches it
|
|
12
|
+
* names — so an error is about the content on the editor's screen, and a fix
|
|
13
|
+
* is a patch on top of it.
|
|
14
|
+
*/
|
|
15
|
+
export type StudioCheckInput = {
|
|
16
|
+
moduleFilePath: ModuleFilePath;
|
|
17
|
+
schemas: Schemas;
|
|
18
|
+
sources: Sources;
|
|
19
|
+
fileLastUpdatedByPatchId: PatchAnalysis["fileLastUpdatedByPatchId"];
|
|
20
|
+
/** How remote hosts are asked about refs. Tests replace it. */
|
|
21
|
+
network?: RemoteNetwork;
|
|
22
|
+
};
|
|
23
|
+
export type StudioModuleCheck = {
|
|
24
|
+
status: "ok";
|
|
25
|
+
errors: Record<SourcePath, ValidationError[]>;
|
|
26
|
+
/** Hosts that did not serve this module's remote files. */
|
|
27
|
+
remoteHosts: RemoteHostProblem[];
|
|
28
|
+
} | {
|
|
29
|
+
status: "unknown-module";
|
|
30
|
+
} | {
|
|
31
|
+
status: "invalid-source";
|
|
32
|
+
message: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Every error in one module: its sources against its schema, and its local
|
|
36
|
+
* files against their bytes.
|
|
37
|
+
*
|
|
38
|
+
* Not filtered the way the publish gate filters (`partitionValidationErrors`):
|
|
39
|
+
* the errors the gate hides because "the server repairs them on save" are the
|
|
40
|
+
* ones this check exists to show, since nothing repairs them in http mode.
|
|
41
|
+
*
|
|
42
|
+
* Remote files are checked too: a stale ref is an error with a fix, and a
|
|
43
|
+
* host that does not serve them is reported once, as a host, rather than as
|
|
44
|
+
* an error on every image it holds (`checkRemoteFiles`).
|
|
45
|
+
*
|
|
46
|
+
* Not yet here: the media directories' own entries, step 5 of the plan.
|
|
47
|
+
*/
|
|
48
|
+
export declare function checkModuleForStudio(ops: ValOps, input: StudioCheckInput): Promise<StudioModuleCheck>;
|
|
49
|
+
export type StudioFixResult = {
|
|
50
|
+
status: "ok";
|
|
51
|
+
patch: Patch;
|
|
52
|
+
remainingErrors: FixPatchRemainingError[];
|
|
53
|
+
} | {
|
|
54
|
+
status: "unknown-module";
|
|
55
|
+
} | {
|
|
56
|
+
status: "not-in-studio";
|
|
57
|
+
} | {
|
|
58
|
+
status: "not-found";
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* The patch that fixes one error, built from the server's own reading of the
|
|
62
|
+
* module rather than from the error the caller saw.
|
|
63
|
+
*
|
|
64
|
+
* The caller names the error by where it is and which fix it wants; the error
|
|
65
|
+
* itself is found again here. That is what keeps a stale or hand-written
|
|
66
|
+
* request from steering `createFixPatch` (CLAUDE.md, "Adding a ValidationFix
|
|
67
|
+
* code"), and it is what lets an error reported at a FIELD be fixed at the
|
|
68
|
+
* media value it belongs to: `validateFiles` reports a wrong width at
|
|
69
|
+
* `…"hero"."width"`, while the fix rewrites `…"hero"`.
|
|
70
|
+
*
|
|
71
|
+
* Only {@link isStudioFix} codes are built. The others need a disk or a
|
|
72
|
+
* remote host this server may not have, and are the CLI's for now.
|
|
73
|
+
*/
|
|
74
|
+
export declare function fixForStudio(ops: ValOps, input: StudioCheckInput & {
|
|
75
|
+
sourcePath: SourcePath;
|
|
76
|
+
fix: ValidationFix;
|
|
77
|
+
}): Promise<StudioFixResult>;
|
|
78
|
+
/**
|
|
79
|
+
* {@link FixFiles} over whatever store this server has: the disk in fs mode,
|
|
80
|
+
* the content service in http mode. A file a pending patch wrote is read from
|
|
81
|
+
* that patch, as `validateFiles` reads it, so a fix is made from the bytes
|
|
82
|
+
* the check was made from.
|
|
83
|
+
*/
|
|
84
|
+
export declare function opsFixFiles(ops: ValOps, fileLastUpdatedByPatchId: PatchAnalysis["fileLastUpdatedByPatchId"]): FixFiles;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function uploadRemoteFile(contentHost: string, project: string, bucket: string, fileHash: string, fileExt: string, fileBuffer:
|
|
1
|
+
export declare function uploadRemoteFile(contentHost: string, project: string, bucket: string, fileHash: string, fileExt: string, fileBuffer: Uint8Array, auth: {
|
|
2
2
|
pat: string;
|
|
3
3
|
} | {
|
|
4
4
|
apiKey: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ModuleFilePath, type SerializedRecordSchema, type SerializedSchema, type SerializedVideoSchema, type Source, type SourcePath, type ValidationError } from "@valbuild/core";
|
|
2
2
|
import { type Patch } from "@valbuild/core/patch";
|
|
3
3
|
import type { FixPatchRemainingError } from "./createFixPatch.js";
|
|
4
|
+
import type { FixFiles } from "./fixFiles.js";
|
|
4
5
|
import type { FixHandlerContext, FixHandlerResult, ModulePatch } from "./fixHandlers.js";
|
|
5
6
|
import { type GalleryEntryKey } from "./galleryEntryKey.js";
|
|
6
7
|
import type { Service } from "./Service.js";
|
|
@@ -129,8 +130,8 @@ export declare function videosetCheckAllFilesPatch({ projectRoot, apply, sourceP
|
|
|
129
130
|
* from the file at the entry's KEY, and write only that, one op per field —
|
|
130
131
|
* `alt` and anything already there are someone's, and stay.
|
|
131
132
|
*/
|
|
132
|
-
export declare function videosetAddMetadataPatch({
|
|
133
|
-
|
|
133
|
+
export declare function videosetAddMetadataPatch({ files, sourcePath, validationError, moduleSource, moduleSchema, }: {
|
|
134
|
+
files: FixFiles;
|
|
134
135
|
sourcePath: SourcePath;
|
|
135
136
|
validationError: ValidationError;
|
|
136
137
|
moduleSource: Source | undefined;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ts = require('typescript');
|
|
4
|
+
var ValRouterCore = require('./ValRouterCore-42772c21.cjs.prod.js');
|
|
5
|
+
var fp = require('@valbuild/core/fp');
|
|
6
|
+
var jsonValuesModule = require('./jsonValuesModule-d1bb8d8f.cjs.prod.js');
|
|
7
|
+
require('@valbuild/core');
|
|
8
|
+
require('@valbuild/shared/internal');
|
|
9
|
+
require('zod');
|
|
10
|
+
require('zod-validation-error');
|
|
11
|
+
require('@valbuild/core/patch');
|
|
12
|
+
require('image-size');
|
|
13
|
+
|
|
14
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
|
|
15
|
+
|
|
16
|
+
var ts__default = /*#__PURE__*/_interopDefault(ts);
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* One `.jsonValues()` entry's content, as it was at a commit.
|
|
20
|
+
*
|
|
21
|
+
* The history pane renders a commit through the real field components, and a
|
|
22
|
+
* `.jsonValues()` record's source is only `{_type:"json"}` markers - the
|
|
23
|
+
* content lives in a separate `*.val.json` per entry, fetched on demand. The
|
|
24
|
+
* live Studio fetches it from `GET /json`; the commit pane had nothing to
|
|
25
|
+
* fetch from, so every entry stayed a marker and rendered as though the field
|
|
26
|
+
* were empty. Which is worse than an error: an empty value is a claim, and it
|
|
27
|
+
* was a false one.
|
|
28
|
+
*
|
|
29
|
+
* ## Why this needs the module's own `.val.ts`
|
|
30
|
+
*
|
|
31
|
+
* There is no derivable relationship between a record key and its entry file.
|
|
32
|
+
* From the example app:
|
|
33
|
+
*
|
|
34
|
+
* "/support/getting-started": c.json(() => import("./content/getting-started.val.json"))
|
|
35
|
+
* "/support/faq": c.json(() => import("./content/faq.val.json"))
|
|
36
|
+
*
|
|
37
|
+
* The key is a route and the path is whatever the author wrote. The only place
|
|
38
|
+
* the two are related is the `import()` literal inside the `.val.ts`, which is
|
|
39
|
+
* why `findJsonEntryFilePathsInSource` reads them out of the AST too. The
|
|
40
|
+
* commit archive stores the module's SOURCE (the markers) and its schema, not
|
|
41
|
+
* its text - so the text is fetched from git at that commit and parsed here.
|
|
42
|
+
*
|
|
43
|
+
* Two round trips per entry, then: the module, and the entry. The module's
|
|
44
|
+
* parse is worth caching per (commit, module) by the caller; nothing here does,
|
|
45
|
+
* because a `ValOps` is not a cache and the endpoint above it is the layer that
|
|
46
|
+
* knows how long a commit stays interesting.
|
|
47
|
+
*/
|
|
48
|
+
async function getJsonEntryAtCommit(ops, commitSha, moduleFilePath, key) {
|
|
49
|
+
const moduleGitPath = ops.gitPathOfModule(moduleFilePath);
|
|
50
|
+
if (fp.result.isErr(moduleGitPath)) {
|
|
51
|
+
return moduleGitPath;
|
|
52
|
+
}
|
|
53
|
+
const moduleRes = await ops.getFileAtCommit(commitSha, moduleGitPath.value, false);
|
|
54
|
+
if (fp.result.isErr(moduleRes)) {
|
|
55
|
+
return moduleRes;
|
|
56
|
+
}
|
|
57
|
+
const entryPath = findEntryImportPath(moduleFilePath, moduleGitPath.value, ValRouterCore.bytesToUtf8(moduleRes.value), key);
|
|
58
|
+
if (fp.result.isErr(entryPath)) {
|
|
59
|
+
return entryPath;
|
|
60
|
+
}
|
|
61
|
+
// The entry path resolved above is PROJECT-relative, like the module path it
|
|
62
|
+
// came from; the git path needs the root in front of it, and the ops are what
|
|
63
|
+
// know the root. Reusing gitPathOfModule rather than re-deriving the prefix:
|
|
64
|
+
// a `.val.json` is not a module, but "project-relative path -> repo path" is
|
|
65
|
+
// the same question and one implementation of it is the point.
|
|
66
|
+
const entryGitPath = ops.gitPathOfModule(entryPath.value);
|
|
67
|
+
if (fp.result.isErr(entryGitPath)) {
|
|
68
|
+
return entryGitPath;
|
|
69
|
+
}
|
|
70
|
+
const entryRes = await ops.getFileAtCommit(commitSha, entryGitPath.value, false);
|
|
71
|
+
if (fp.result.isErr(entryRes)) {
|
|
72
|
+
return entryRes;
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
return fp.result.ok(JSON.parse(ValRouterCore.bytesToUtf8(entryRes.value)));
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return fp.result.err({
|
|
78
|
+
kind: "file-unavailable",
|
|
79
|
+
gitPath: entryGitPath.value,
|
|
80
|
+
message: `not valid JSON at this commit: ${err instanceof Error ? err.message : String(err)}`
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* The `import()` path a key's entry is behind, from the module's text.
|
|
87
|
+
*
|
|
88
|
+
* Exported for its own test: this is the part with a real chance of being
|
|
89
|
+
* wrong, and it is pure.
|
|
90
|
+
*/
|
|
91
|
+
function findEntryImportPath(moduleFilePath,
|
|
92
|
+
/**
|
|
93
|
+
* The same module, repository-relative, for error reporting only.
|
|
94
|
+
*
|
|
95
|
+
* A `gitPath` in a HistoryError is a REPOSITORY path - that is what every
|
|
96
|
+
* other history helper reports, and what a reader can paste into a `git show`
|
|
97
|
+
* - so reporting the project-relative ModuleFilePath here named a file that
|
|
98
|
+
* does not exist at that path for any project not rooted at the repo root.
|
|
99
|
+
*/
|
|
100
|
+
moduleGitPath, valTsSource, key) {
|
|
101
|
+
const sourceFile = ts__default["default"].createSourceFile(moduleFilePath, valTsSource, ts__default["default"].ScriptTarget.ES2015, true);
|
|
102
|
+
let analysis;
|
|
103
|
+
try {
|
|
104
|
+
analysis = jsonValuesModule.analyzeValModule(sourceFile);
|
|
105
|
+
} catch (err) {
|
|
106
|
+
// `file-unavailable` rather than a kind of its own: the module's TEXT is
|
|
107
|
+
// the file we could not use, and adding a wire kind for "unparseable"
|
|
108
|
+
// would be a new case every reader has to handle to say the same thing.
|
|
109
|
+
return fp.result.err({
|
|
110
|
+
kind: "file-unavailable",
|
|
111
|
+
gitPath: moduleGitPath,
|
|
112
|
+
message: `could not parse the module at this commit: ${err instanceof Error ? err.message : String(err)}`
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (fp.result.isErr(analysis)) {
|
|
116
|
+
return fp.result.err({
|
|
117
|
+
kind: "file-unavailable",
|
|
118
|
+
gitPath: moduleGitPath,
|
|
119
|
+
message: "could not read the module at this commit"
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const entries = jsonValuesModule.analyzeJsonValuesEntries(analysis.value.source);
|
|
123
|
+
const entry = entries.get(key);
|
|
124
|
+
if (entry === undefined) {
|
|
125
|
+
/*
|
|
126
|
+
* Reported, not treated as empty. A key with no entry at this commit means
|
|
127
|
+
* the entry was added later - so "there is nothing to show for it here" is
|
|
128
|
+
* the true answer, and rendering an empty field instead would say the
|
|
129
|
+
* author had left it blank.
|
|
130
|
+
*/
|
|
131
|
+
return fp.result.err({
|
|
132
|
+
kind: "file-unavailable",
|
|
133
|
+
gitPath: moduleGitPath,
|
|
134
|
+
message: `'${key}' had no entry at this commit`
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
const moduleDir = ValRouterCore.posixPath.dirname(moduleFilePath);
|
|
138
|
+
return fp.result.ok(ValRouterCore.posixPath.join(moduleDir, entry.importPath));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
exports.findEntryImportPath = findEntryImportPath;
|
|
142
|
+
exports.getJsonEntryAtCommit = getJsonEntryAtCommit;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { F as bytesToUtf8, G as posixPath } from './ValRouterCore-7ca3dc68.esm.js';
|
|
3
|
+
import { result } from '@valbuild/core/fp';
|
|
4
|
+
import { a as analyzeValModule, c as analyzeJsonValuesEntries } from './jsonValuesModule-427d68c8.esm.js';
|
|
5
|
+
import '@valbuild/core';
|
|
6
|
+
import '@valbuild/shared/internal';
|
|
7
|
+
import 'zod';
|
|
8
|
+
import 'zod-validation-error';
|
|
9
|
+
import '@valbuild/core/patch';
|
|
10
|
+
import 'image-size';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* One `.jsonValues()` entry's content, as it was at a commit.
|
|
14
|
+
*
|
|
15
|
+
* The history pane renders a commit through the real field components, and a
|
|
16
|
+
* `.jsonValues()` record's source is only `{_type:"json"}` markers - the
|
|
17
|
+
* content lives in a separate `*.val.json` per entry, fetched on demand. The
|
|
18
|
+
* live Studio fetches it from `GET /json`; the commit pane had nothing to
|
|
19
|
+
* fetch from, so every entry stayed a marker and rendered as though the field
|
|
20
|
+
* were empty. Which is worse than an error: an empty value is a claim, and it
|
|
21
|
+
* was a false one.
|
|
22
|
+
*
|
|
23
|
+
* ## Why this needs the module's own `.val.ts`
|
|
24
|
+
*
|
|
25
|
+
* There is no derivable relationship between a record key and its entry file.
|
|
26
|
+
* From the example app:
|
|
27
|
+
*
|
|
28
|
+
* "/support/getting-started": c.json(() => import("./content/getting-started.val.json"))
|
|
29
|
+
* "/support/faq": c.json(() => import("./content/faq.val.json"))
|
|
30
|
+
*
|
|
31
|
+
* The key is a route and the path is whatever the author wrote. The only place
|
|
32
|
+
* the two are related is the `import()` literal inside the `.val.ts`, which is
|
|
33
|
+
* why `findJsonEntryFilePathsInSource` reads them out of the AST too. The
|
|
34
|
+
* commit archive stores the module's SOURCE (the markers) and its schema, not
|
|
35
|
+
* its text - so the text is fetched from git at that commit and parsed here.
|
|
36
|
+
*
|
|
37
|
+
* Two round trips per entry, then: the module, and the entry. The module's
|
|
38
|
+
* parse is worth caching per (commit, module) by the caller; nothing here does,
|
|
39
|
+
* because a `ValOps` is not a cache and the endpoint above it is the layer that
|
|
40
|
+
* knows how long a commit stays interesting.
|
|
41
|
+
*/
|
|
42
|
+
async function getJsonEntryAtCommit(ops, commitSha, moduleFilePath, key) {
|
|
43
|
+
const moduleGitPath = ops.gitPathOfModule(moduleFilePath);
|
|
44
|
+
if (result.isErr(moduleGitPath)) {
|
|
45
|
+
return moduleGitPath;
|
|
46
|
+
}
|
|
47
|
+
const moduleRes = await ops.getFileAtCommit(commitSha, moduleGitPath.value, false);
|
|
48
|
+
if (result.isErr(moduleRes)) {
|
|
49
|
+
return moduleRes;
|
|
50
|
+
}
|
|
51
|
+
const entryPath = findEntryImportPath(moduleFilePath, moduleGitPath.value, bytesToUtf8(moduleRes.value), key);
|
|
52
|
+
if (result.isErr(entryPath)) {
|
|
53
|
+
return entryPath;
|
|
54
|
+
}
|
|
55
|
+
// The entry path resolved above is PROJECT-relative, like the module path it
|
|
56
|
+
// came from; the git path needs the root in front of it, and the ops are what
|
|
57
|
+
// know the root. Reusing gitPathOfModule rather than re-deriving the prefix:
|
|
58
|
+
// a `.val.json` is not a module, but "project-relative path -> repo path" is
|
|
59
|
+
// the same question and one implementation of it is the point.
|
|
60
|
+
const entryGitPath = ops.gitPathOfModule(entryPath.value);
|
|
61
|
+
if (result.isErr(entryGitPath)) {
|
|
62
|
+
return entryGitPath;
|
|
63
|
+
}
|
|
64
|
+
const entryRes = await ops.getFileAtCommit(commitSha, entryGitPath.value, false);
|
|
65
|
+
if (result.isErr(entryRes)) {
|
|
66
|
+
return entryRes;
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
return result.ok(JSON.parse(bytesToUtf8(entryRes.value)));
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return result.err({
|
|
72
|
+
kind: "file-unavailable",
|
|
73
|
+
gitPath: entryGitPath.value,
|
|
74
|
+
message: `not valid JSON at this commit: ${err instanceof Error ? err.message : String(err)}`
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The `import()` path a key's entry is behind, from the module's text.
|
|
81
|
+
*
|
|
82
|
+
* Exported for its own test: this is the part with a real chance of being
|
|
83
|
+
* wrong, and it is pure.
|
|
84
|
+
*/
|
|
85
|
+
function findEntryImportPath(moduleFilePath,
|
|
86
|
+
/**
|
|
87
|
+
* The same module, repository-relative, for error reporting only.
|
|
88
|
+
*
|
|
89
|
+
* A `gitPath` in a HistoryError is a REPOSITORY path - that is what every
|
|
90
|
+
* other history helper reports, and what a reader can paste into a `git show`
|
|
91
|
+
* - so reporting the project-relative ModuleFilePath here named a file that
|
|
92
|
+
* does not exist at that path for any project not rooted at the repo root.
|
|
93
|
+
*/
|
|
94
|
+
moduleGitPath, valTsSource, key) {
|
|
95
|
+
const sourceFile = ts.createSourceFile(moduleFilePath, valTsSource, ts.ScriptTarget.ES2015, true);
|
|
96
|
+
let analysis;
|
|
97
|
+
try {
|
|
98
|
+
analysis = analyzeValModule(sourceFile);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
// `file-unavailable` rather than a kind of its own: the module's TEXT is
|
|
101
|
+
// the file we could not use, and adding a wire kind for "unparseable"
|
|
102
|
+
// would be a new case every reader has to handle to say the same thing.
|
|
103
|
+
return result.err({
|
|
104
|
+
kind: "file-unavailable",
|
|
105
|
+
gitPath: moduleGitPath,
|
|
106
|
+
message: `could not parse the module at this commit: ${err instanceof Error ? err.message : String(err)}`
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
if (result.isErr(analysis)) {
|
|
110
|
+
return result.err({
|
|
111
|
+
kind: "file-unavailable",
|
|
112
|
+
gitPath: moduleGitPath,
|
|
113
|
+
message: "could not read the module at this commit"
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const entries = analyzeJsonValuesEntries(analysis.value.source);
|
|
117
|
+
const entry = entries.get(key);
|
|
118
|
+
if (entry === undefined) {
|
|
119
|
+
/*
|
|
120
|
+
* Reported, not treated as empty. A key with no entry at this commit means
|
|
121
|
+
* the entry was added later - so "there is nothing to show for it here" is
|
|
122
|
+
* the true answer, and rendering an empty field instead would say the
|
|
123
|
+
* author had left it blank.
|
|
124
|
+
*/
|
|
125
|
+
return result.err({
|
|
126
|
+
kind: "file-unavailable",
|
|
127
|
+
gitPath: moduleGitPath,
|
|
128
|
+
message: `'${key}' had no entry at this commit`
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
const moduleDir = posixPath.dirname(moduleFilePath);
|
|
132
|
+
return result.ok(posixPath.join(moduleDir, entry.importPath));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export { findEntryImportPath, getJsonEntryAtCommit };
|