@valbuild/server 0.15.0 → 0.16.1
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/dist/declarations/src/LocalValServer.d.ts +22 -0
- package/dist/declarations/src/SerializedModuleContent.d.ts +17 -2
- package/dist/declarations/src/Service.d.ts +2 -1
- package/dist/declarations/src/createFixPatch.d.ts +8 -0
- package/dist/declarations/src/hosting.d.ts +8 -0
- package/dist/declarations/src/index.d.ts +3 -0
- package/dist/declarations/src/patch/validation.d.ts +4 -0
- package/dist/valbuild-server.cjs.dev.js +179 -26
- package/dist/valbuild-server.cjs.prod.js +179 -26
- package/dist/valbuild-server.esm.js +177 -28
- package/package.json +3 -2
- package/src/ProxyValServer.ts +11 -3
- package/src/SerializedModuleContent.ts +34 -7
- package/src/Service.ts +41 -21
- package/src/ValModuleLoader.ts +1 -1
- package/src/createFixPatch.ts +175 -0
- package/src/hosting.ts +13 -0
- package/src/index.ts +3 -0
- package/src/readValFile.ts +39 -21
package/src/ProxyValServer.ts
CHANGED
|
@@ -18,6 +18,7 @@ export type ProxyValServerOptions = {
|
|
|
18
18
|
valBuildUrl: string;
|
|
19
19
|
gitCommit: string;
|
|
20
20
|
gitBranch: string;
|
|
21
|
+
valName: string;
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
export class ProxyValServer implements ValServer {
|
|
@@ -112,9 +113,10 @@ export class ProxyValServer implements ValServer {
|
|
|
112
113
|
}
|
|
113
114
|
|
|
114
115
|
async session(req: express.Request, res: express.Response): Promise<void> {
|
|
116
|
+
console.log("hit session");
|
|
115
117
|
return this.withAuth(req, res, async (data) => {
|
|
116
118
|
const url = new URL(
|
|
117
|
-
|
|
119
|
+
`/api/val/${this.options.valName}/auth/session`,
|
|
118
120
|
this.options.valBuildUrl
|
|
119
121
|
);
|
|
120
122
|
const fetchRes = await fetch(url, {
|
|
@@ -233,7 +235,10 @@ export class ProxyValServer implements ValServer {
|
|
|
233
235
|
project: string;
|
|
234
236
|
token: string;
|
|
235
237
|
} | null> {
|
|
236
|
-
const url = new URL(
|
|
238
|
+
const url = new URL(
|
|
239
|
+
`/api/val/${this.options.valName}/auth/token`,
|
|
240
|
+
this.options.valBuildUrl
|
|
241
|
+
);
|
|
237
242
|
url.searchParams.set("code", encodeURIComponent(code));
|
|
238
243
|
return fetch(url, {
|
|
239
244
|
method: "POST",
|
|
@@ -262,7 +267,10 @@ export class ProxyValServer implements ValServer {
|
|
|
262
267
|
}
|
|
263
268
|
|
|
264
269
|
private getAuthorizeUrl(publicValApiRoute: string, token: string): string {
|
|
265
|
-
const url = new URL(
|
|
270
|
+
const url = new URL(
|
|
271
|
+
`/auth/${this.options.valName}/authorize`,
|
|
272
|
+
this.options.valBuildUrl
|
|
273
|
+
);
|
|
266
274
|
url.searchParams.set(
|
|
267
275
|
"redirect_uri",
|
|
268
276
|
encodeURIComponent(`${publicValApiRoute}/callback`)
|
|
@@ -1,8 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
type Source,
|
|
3
|
+
type SerializedSchema,
|
|
4
|
+
ValidationErrors,
|
|
5
|
+
} from "@valbuild/core";
|
|
6
|
+
import { ModuleId, type SourcePath } from "@valbuild/core/src/val";
|
|
3
7
|
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
export const FATAL_ERROR_TYPES = [
|
|
9
|
+
"no-schema",
|
|
10
|
+
"no-source",
|
|
11
|
+
"invalid-id",
|
|
12
|
+
"no-module",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export type SerializedModuleContent =
|
|
16
|
+
| {
|
|
17
|
+
source: Source;
|
|
18
|
+
schema: SerializedSchema;
|
|
19
|
+
path: SourcePath;
|
|
20
|
+
errors: false;
|
|
21
|
+
}
|
|
22
|
+
| {
|
|
23
|
+
source?: Source;
|
|
24
|
+
schema?: SerializedSchema;
|
|
25
|
+
path?: SourcePath;
|
|
26
|
+
errors: {
|
|
27
|
+
invalidModuleId?: ModuleId;
|
|
28
|
+
validation?: ValidationErrors;
|
|
29
|
+
fatal?: {
|
|
30
|
+
message: string;
|
|
31
|
+
stack?: string[];
|
|
32
|
+
type?: (typeof FATAL_ERROR_TYPES)[number];
|
|
33
|
+
}[];
|
|
34
|
+
};
|
|
35
|
+
};
|
package/src/Service.ts
CHANGED
|
@@ -34,7 +34,8 @@ export async function createService(
|
|
|
34
34
|
host: IValFSHost = {
|
|
35
35
|
...ts.sys,
|
|
36
36
|
writeFile: fs.writeFileSync,
|
|
37
|
-
}
|
|
37
|
+
},
|
|
38
|
+
loader?: ValModuleLoader
|
|
38
39
|
): Promise<Service> {
|
|
39
40
|
const compilerOptions = getCompilerOptions(projectRoot, host);
|
|
40
41
|
const sourceFileHandler = new ValSourceFileHandler(
|
|
@@ -42,14 +43,12 @@ export async function createService(
|
|
|
42
43
|
compilerOptions,
|
|
43
44
|
host
|
|
44
45
|
);
|
|
45
|
-
const loader = new ValModuleLoader(
|
|
46
|
-
projectRoot,
|
|
47
|
-
compilerOptions,
|
|
48
|
-
sourceFileHandler,
|
|
49
|
-
host
|
|
50
|
-
);
|
|
51
46
|
const module = await newQuickJSWASMModule();
|
|
52
|
-
const runtime = await newValQuickJSRuntime(
|
|
47
|
+
const runtime = await newValQuickJSRuntime(
|
|
48
|
+
module,
|
|
49
|
+
loader ||
|
|
50
|
+
new ValModuleLoader(projectRoot, compilerOptions, sourceFileHandler, host)
|
|
51
|
+
);
|
|
53
52
|
return new Service(opts, sourceFileHandler, runtime);
|
|
54
53
|
}
|
|
55
54
|
|
|
@@ -74,19 +73,40 @@ export class Service {
|
|
|
74
73
|
this.runtime
|
|
75
74
|
);
|
|
76
75
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
if (valModule.source && valModule.schema) {
|
|
77
|
+
const resolved = Internal.resolvePath(
|
|
78
|
+
modulePath,
|
|
79
|
+
valModule.source,
|
|
80
|
+
valModule.schema
|
|
81
|
+
);
|
|
82
|
+
const sourcePath = [moduleId, resolved.path].join(".") as SourcePath;
|
|
83
|
+
return {
|
|
84
|
+
path: sourcePath,
|
|
85
|
+
schema:
|
|
86
|
+
resolved.schema instanceof Schema<SelectorSource>
|
|
87
|
+
? resolved.schema.serialize()
|
|
88
|
+
: resolved.schema,
|
|
89
|
+
source: resolved.source,
|
|
90
|
+
errors:
|
|
91
|
+
valModule.errors &&
|
|
92
|
+
valModule.errors.validation &&
|
|
93
|
+
valModule.errors.validation[sourcePath]
|
|
94
|
+
? {
|
|
95
|
+
validation: valModule.errors.validation[sourcePath]
|
|
96
|
+
? {
|
|
97
|
+
[sourcePath]: valModule.errors.validation[sourcePath],
|
|
98
|
+
}
|
|
99
|
+
: undefined,
|
|
100
|
+
fatal:
|
|
101
|
+
valModule.errors && valModule.errors.fatal
|
|
102
|
+
? valModule.errors.fatal
|
|
103
|
+
: undefined,
|
|
104
|
+
}
|
|
105
|
+
: false,
|
|
106
|
+
};
|
|
107
|
+
} else {
|
|
108
|
+
return valModule;
|
|
109
|
+
}
|
|
90
110
|
}
|
|
91
111
|
|
|
92
112
|
async patch(
|
package/src/ValModuleLoader.ts
CHANGED
|
@@ -60,7 +60,7 @@ export class ValModuleLoader {
|
|
|
60
60
|
// allowJs: true,
|
|
61
61
|
// rootDir: this.compilerOptions.rootDir,
|
|
62
62
|
module: ts.ModuleKind.ESNext,
|
|
63
|
-
target: ts.ScriptTarget.
|
|
63
|
+
target: ts.ScriptTarget.ES2015, // QuickJS supports a lot of ES2020: https://test262.report/, however not all cases are in that report (e.g. export const {} = {})
|
|
64
64
|
// moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
65
65
|
// target: ts.ScriptTarget.ES2020, // QuickJs runs in ES2020 so we must use that
|
|
66
66
|
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { FILE_REF_PROP, SourcePath, ValidationError } from "@valbuild/core";
|
|
2
|
+
import { Patch, sourceToPatchPath } from "@valbuild/core/patch";
|
|
3
|
+
import sizeOf from "image-size";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import crypto from "crypto";
|
|
7
|
+
|
|
8
|
+
// TODO: find a better name? transformFixesToPatch?
|
|
9
|
+
export async function createFixPatch(
|
|
10
|
+
config: { projectRoot: string },
|
|
11
|
+
apply: boolean,
|
|
12
|
+
sourcePath: SourcePath,
|
|
13
|
+
validationError: ValidationError
|
|
14
|
+
): Promise<{ patch: Patch; remainingErrors: ValidationError[] } | undefined> {
|
|
15
|
+
async function getImageMetadata() {
|
|
16
|
+
const maybeRef =
|
|
17
|
+
validationError.value &&
|
|
18
|
+
typeof validationError.value === "object" &&
|
|
19
|
+
FILE_REF_PROP in validationError.value &&
|
|
20
|
+
typeof validationError.value[FILE_REF_PROP] === "string"
|
|
21
|
+
? validationError.value[FILE_REF_PROP]
|
|
22
|
+
: undefined;
|
|
23
|
+
|
|
24
|
+
if (!maybeRef) {
|
|
25
|
+
// TODO:
|
|
26
|
+
throw Error("Cannot fix image without a file reference");
|
|
27
|
+
}
|
|
28
|
+
const localFile = path.join(config.projectRoot, maybeRef);
|
|
29
|
+
const buffer = fs.readFileSync(localFile);
|
|
30
|
+
const sha256 = await getSHA256Hash(buffer);
|
|
31
|
+
const imageSize = sizeOf(buffer);
|
|
32
|
+
return {
|
|
33
|
+
...imageSize,
|
|
34
|
+
sha256,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const remainingErrors: ValidationError[] = [];
|
|
38
|
+
const patch: Patch = [];
|
|
39
|
+
for (const fix of validationError.fixes || []) {
|
|
40
|
+
if (fix === "image:replace-metadata" || fix === "image:add-metadata") {
|
|
41
|
+
const imageMetadata = await getImageMetadata();
|
|
42
|
+
if (
|
|
43
|
+
imageMetadata.width === undefined ||
|
|
44
|
+
imageMetadata.height === undefined ||
|
|
45
|
+
imageMetadata.sha256 === undefined
|
|
46
|
+
) {
|
|
47
|
+
remainingErrors.push({
|
|
48
|
+
...validationError,
|
|
49
|
+
message: "Failed to get image metadata",
|
|
50
|
+
fixes: undefined,
|
|
51
|
+
});
|
|
52
|
+
} else if (fix === "image:replace-metadata") {
|
|
53
|
+
const currentValue = validationError.value;
|
|
54
|
+
const metadataIsCorrect =
|
|
55
|
+
// metadata is a prop that is an object
|
|
56
|
+
typeof currentValue === "object" &&
|
|
57
|
+
currentValue &&
|
|
58
|
+
"metadata" in currentValue &&
|
|
59
|
+
currentValue.metadata &&
|
|
60
|
+
typeof currentValue.metadata === "object" &&
|
|
61
|
+
// sha256 is correct
|
|
62
|
+
"sha256" in currentValue.metadata &&
|
|
63
|
+
currentValue.metadata.sha256 === imageMetadata.sha256 &&
|
|
64
|
+
// width is correct
|
|
65
|
+
"width" in currentValue.metadata &&
|
|
66
|
+
currentValue.metadata.width === imageMetadata.width &&
|
|
67
|
+
// height is correct
|
|
68
|
+
"height" in currentValue.metadata &&
|
|
69
|
+
currentValue.metadata.height === imageMetadata.height;
|
|
70
|
+
|
|
71
|
+
// skips if the metadata is already correct
|
|
72
|
+
if (!metadataIsCorrect) {
|
|
73
|
+
if (apply) {
|
|
74
|
+
patch.push({
|
|
75
|
+
op: "replace",
|
|
76
|
+
path: sourceToPatchPath(sourcePath).concat("metadata"),
|
|
77
|
+
value: {
|
|
78
|
+
width: imageMetadata.width,
|
|
79
|
+
height: imageMetadata.height,
|
|
80
|
+
sha256: imageMetadata.sha256,
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
if (
|
|
85
|
+
typeof currentValue === "object" &&
|
|
86
|
+
currentValue &&
|
|
87
|
+
"metadata" in currentValue &&
|
|
88
|
+
currentValue.metadata &&
|
|
89
|
+
typeof currentValue.metadata === "object"
|
|
90
|
+
) {
|
|
91
|
+
if (
|
|
92
|
+
!("sha256" in currentValue.metadata) ||
|
|
93
|
+
currentValue.metadata.sha256 !== imageMetadata.sha256
|
|
94
|
+
) {
|
|
95
|
+
remainingErrors.push({
|
|
96
|
+
message:
|
|
97
|
+
"Image metadata sha256 is incorrect! Found: " +
|
|
98
|
+
("sha256" in currentValue.metadata
|
|
99
|
+
? currentValue.metadata.sha256
|
|
100
|
+
: "<empty>") +
|
|
101
|
+
". Expected: " +
|
|
102
|
+
imageMetadata.sha256 +
|
|
103
|
+
".",
|
|
104
|
+
fixes: undefined,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (
|
|
108
|
+
!("width" in currentValue.metadata) ||
|
|
109
|
+
currentValue.metadata.width !== imageMetadata.width
|
|
110
|
+
) {
|
|
111
|
+
remainingErrors.push({
|
|
112
|
+
message:
|
|
113
|
+
"Image metadata width is incorrect! Found: " +
|
|
114
|
+
("width" in currentValue.metadata
|
|
115
|
+
? currentValue.metadata.width
|
|
116
|
+
: "<empty>") +
|
|
117
|
+
". Expected: " +
|
|
118
|
+
imageMetadata.width,
|
|
119
|
+
fixes: undefined,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
if (
|
|
123
|
+
!("height" in currentValue.metadata) ||
|
|
124
|
+
currentValue.metadata.height !== imageMetadata.height
|
|
125
|
+
) {
|
|
126
|
+
remainingErrors.push({
|
|
127
|
+
message:
|
|
128
|
+
"Image metadata height is incorrect! Found: " +
|
|
129
|
+
("height" in currentValue.metadata
|
|
130
|
+
? currentValue.metadata.height
|
|
131
|
+
: "<empty>") +
|
|
132
|
+
". Expected: " +
|
|
133
|
+
imageMetadata.height,
|
|
134
|
+
fixes: undefined,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
} else {
|
|
138
|
+
remainingErrors.push({
|
|
139
|
+
...validationError,
|
|
140
|
+
message: "Image metadata is not an object!",
|
|
141
|
+
fixes: undefined,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} else if (fix === "image:add-metadata") {
|
|
147
|
+
patch.push({
|
|
148
|
+
op: "add",
|
|
149
|
+
path: sourceToPatchPath(sourcePath).concat("metadata"),
|
|
150
|
+
value: {
|
|
151
|
+
width: imageMetadata.width,
|
|
152
|
+
height: imageMetadata.height,
|
|
153
|
+
sha256: imageMetadata.sha256,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (!validationError.fixes || validationError.fixes.length === 0) {
|
|
160
|
+
remainingErrors.push(validationError);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
patch,
|
|
164
|
+
remainingErrors,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const getSHA256Hash = async (bits: Uint8Array) => {
|
|
169
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", bits);
|
|
170
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
171
|
+
const hash = hashArray
|
|
172
|
+
.map((item) => item.toString(16).padStart(2, "0"))
|
|
173
|
+
.join("");
|
|
174
|
+
return hash;
|
|
175
|
+
};
|
package/src/hosting.ts
CHANGED
|
@@ -73,6 +73,14 @@ type ValServerOverrides = Partial<{
|
|
|
73
73
|
* @example "https://app.val.build"
|
|
74
74
|
*/
|
|
75
75
|
valBuildUrl: string;
|
|
76
|
+
/**
|
|
77
|
+
* The full name of this Val project.
|
|
78
|
+
*
|
|
79
|
+
* Typically this is set using the VAL_NAME env var.
|
|
80
|
+
*
|
|
81
|
+
* @example "myorg/my-project"
|
|
82
|
+
*/
|
|
83
|
+
valName: string;
|
|
76
84
|
}>;
|
|
77
85
|
|
|
78
86
|
async function _createRequestListener(
|
|
@@ -119,6 +127,10 @@ async function initHandlerOptions(
|
|
|
119
127
|
if (!maybeGitBranch) {
|
|
120
128
|
throw new Error("VAL_GIT_BRANCH env var must be set in proxy mode");
|
|
121
129
|
}
|
|
130
|
+
const maybeValName = opts.gitBranch || process.env.VAL_NAME;
|
|
131
|
+
if (!maybeValName) {
|
|
132
|
+
throw new Error("VAL_NAME env var must be set in proxy mode");
|
|
133
|
+
}
|
|
122
134
|
return {
|
|
123
135
|
mode: "proxy",
|
|
124
136
|
route,
|
|
@@ -127,6 +139,7 @@ async function initHandlerOptions(
|
|
|
127
139
|
valBuildUrl,
|
|
128
140
|
gitCommit: maybeGitCommit,
|
|
129
141
|
gitBranch: maybeGitBranch,
|
|
142
|
+
valName: maybeValName,
|
|
130
143
|
};
|
|
131
144
|
} else {
|
|
132
145
|
const service = await createService(process.cwd(), opts);
|
package/src/index.ts
CHANGED
|
@@ -10,3 +10,6 @@ export type { IValFSHost } from "./ValFSHost";
|
|
|
10
10
|
export type { ValFS } from "./ValFS";
|
|
11
11
|
export { patchSourceFile } from "./patchValFile";
|
|
12
12
|
export { formatSyntaxErrorTree } from "./patch/ts/syntax";
|
|
13
|
+
export { LocalValServer } from "./LocalValServer";
|
|
14
|
+
export { createFixPatch } from "./createFixPatch";
|
|
15
|
+
export { PatchJSON } from "./patch/validation";
|
package/src/readValFile.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ModuleId } from "@valbuild/core";
|
|
1
2
|
import path from "path";
|
|
2
3
|
import { QuickJSRuntime } from "quickjs-emscripten";
|
|
3
4
|
import { SerializedModuleContent } from "./SerializedModuleContent";
|
|
@@ -16,6 +17,10 @@ globalThis.valModule = {
|
|
|
16
17
|
id: valModule?.default && Internal.getValPath(valModule?.default),
|
|
17
18
|
schema: valModule?.default && Internal.getSchema(valModule?.default)?.serialize(),
|
|
18
19
|
source: valModule?.default && Internal.getRawSource(valModule?.default),
|
|
20
|
+
validation: valModule?.default && Internal.getSchema(valModule?.default)?.validate(
|
|
21
|
+
valModule?.default && Internal.getValPath(valModule?.default) || "/",
|
|
22
|
+
valModule?.default && Internal.getRawSource(valModule?.default)
|
|
23
|
+
)
|
|
19
24
|
};
|
|
20
25
|
`;
|
|
21
26
|
const result = context.evalCode(
|
|
@@ -23,48 +28,61 @@ globalThis.valModule = {
|
|
|
23
28
|
// Synthetic module name
|
|
24
29
|
path.join(path.dirname(valConfigPath), "<val>")
|
|
25
30
|
);
|
|
31
|
+
const fatalErrors: string[] = [];
|
|
26
32
|
if (result.error) {
|
|
27
33
|
const error = result.error.consume(context.dump);
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
return {
|
|
35
|
+
errors: {
|
|
36
|
+
invalidModuleId: id as ModuleId,
|
|
37
|
+
fatal: [
|
|
38
|
+
{
|
|
39
|
+
message: `${error.name || "Unknown error"}: ${
|
|
40
|
+
error.message || "<no message>"
|
|
41
|
+
}`,
|
|
42
|
+
stack: error.stack,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
};
|
|
35
47
|
} else {
|
|
36
48
|
result.value.dispose();
|
|
37
49
|
const valModule = context
|
|
38
50
|
.getProp(context.global, "valModule")
|
|
39
51
|
.consume(context.dump);
|
|
40
52
|
|
|
41
|
-
const errors: string[] = [];
|
|
42
|
-
|
|
43
53
|
if (!valModule) {
|
|
44
|
-
|
|
54
|
+
fatalErrors.push(`Could not find any modules at: ${id}`);
|
|
45
55
|
} else {
|
|
46
56
|
if (valModule.id !== id) {
|
|
47
|
-
|
|
57
|
+
fatalErrors.push(
|
|
58
|
+
`Expected val id: '${id}' but got: '${valModule.id}'`
|
|
59
|
+
);
|
|
48
60
|
}
|
|
49
61
|
if (!valModule?.schema) {
|
|
50
|
-
|
|
62
|
+
fatalErrors.push(`Expected val id: '${id}' to have a schema`);
|
|
51
63
|
}
|
|
52
64
|
if (!valModule?.source) {
|
|
53
|
-
|
|
65
|
+
fatalErrors.push(`Expected val id: '${id}' to have a source`);
|
|
54
66
|
}
|
|
55
67
|
}
|
|
56
|
-
|
|
57
|
-
if (
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
68
|
+
let errors: SerializedModuleContent["errors"] = false;
|
|
69
|
+
if (fatalErrors.length > 0) {
|
|
70
|
+
errors = {
|
|
71
|
+
invalidModuleId: valModule.id !== id ? (id as ModuleId) : undefined,
|
|
72
|
+
fatal: fatalErrors.map((message) => ({ message })),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (valModule?.validation) {
|
|
76
|
+
errors = {
|
|
77
|
+
...(errors ? errors : {}),
|
|
78
|
+
validation: valModule.validation,
|
|
79
|
+
};
|
|
63
80
|
}
|
|
64
81
|
return {
|
|
65
|
-
path: valModule.id, //
|
|
82
|
+
path: valModule.id, // NOTE: we use path here, since SerializedModuleContent (maybe bad name?) can be used for whole modules as well as subparts of modules
|
|
66
83
|
source: valModule.source,
|
|
67
84
|
schema: valModule.schema,
|
|
85
|
+
errors,
|
|
68
86
|
};
|
|
69
87
|
}
|
|
70
88
|
} finally {
|